viewcvs-web r137 - in trunk: . bin docs lib lib/vcauth lib/vcauth/forbidden lib/vcauth/forbiddenre lib/vcauth/svnauthz lib/vclib lib/vclib/ccvs lib/vclib/ccvs/rcsparse lib/vclib/svn notes notes/logo scripts templates templates-contrib/newvc templates-contrib/tabbed/templates templates-contrib/tabbed/templates/docroot templates-contrib/tabbed/templates/docroot/images templates-contrib/tabbed/templates/include templates-contrib/viewsvn templates/docroot templates/docroot/images templates/include tparse viewvc.org viewvc.org/images viewvc.org/nightly windows www



Author: ovitters
Date: Mon Nov  3 09:57:02 2008
New Revision: 137
URL: http://svn.gnome.org/viewvc/viewcvs-web?rev=137&view=rev

Log:
Merge latest change made upstream with local version.


Added:
   trunk/lib/vcauth/forbiddenre/
      - copied from r136, /tags/viewcvs-2008-11-03/lib/vcauth/forbiddenre/
   trunk/lib/vclib/ccvs/bincvs.py
      - copied unchanged from r136, /tags/viewcvs-2008-11-03/lib/vclib/ccvs/bincvs.py
   trunk/lib/vclib/ccvs/ccvs.py
      - copied unchanged from r136, /tags/viewcvs-2008-11-03/lib/vclib/ccvs/ccvs.py
   trunk/lib/vclib/svn/svn_ra.py
      - copied unchanged from r136, /tags/viewcvs-2008-11-03/lib/vclib/svn/svn_ra.py
   trunk/lib/vclib/svn/svn_repos.py
      - copied unchanged from r136, /tags/viewcvs-2008-11-03/lib/vclib/svn/svn_repos.py
   trunk/notes/logo/
      - copied from r136, /tags/viewcvs-2008-11-03/notes/logo/
   trunk/templates-contrib/newvc/
      - copied from r136, /tags/viewcvs-2008-11-03/templates-contrib/newvc/
   trunk/templates-contrib/tabbed/templates/docroot/images/lock.png
      - copied unchanged from r136, /tags/viewcvs-2008-11-03/templates-contrib/tabbed/templates/docroot/images/lock.png
   trunk/templates-contrib/tabbed/templates/include/fileview.ezt
      - copied unchanged from r136, /tags/viewcvs-2008-11-03/templates-contrib/tabbed/templates/include/fileview.ezt
   trunk/templates-contrib/tabbed/templates/include/props.ezt
      - copied unchanged from r136, /tags/viewcvs-2008-11-03/templates-contrib/tabbed/templates/include/props.ezt
   trunk/templates-contrib/viewsvn/
      - copied from r136, /tags/viewcvs-2008-11-03/templates-contrib/viewsvn/
   trunk/templates/docroot/images/favicon.ico
      - copied unchanged from r136, /tags/viewcvs-2008-11-03/templates/docroot/images/favicon.ico
   trunk/templates/docroot/images/lock.png
      - copied unchanged from r136, /tags/viewcvs-2008-11-03/templates/docroot/images/lock.png
   trunk/templates/docroot/images/viewvc-logo.png
      - copied unchanged from r136, /tags/viewcvs-2008-11-03/templates/docroot/images/viewvc-logo.png
   trunk/templates/file.ezt
      - copied unchanged from r136, /tags/viewcvs-2008-11-03/templates/file.ezt
   trunk/templates/include/props.ezt
      - copied unchanged from r136, /tags/viewcvs-2008-11-03/templates/include/props.ezt
   trunk/viewvc.org/favicon.ico
      - copied unchanged from r136, /tags/viewcvs-2008-11-03/viewvc.org/favicon.ico
Modified:
   trunk/   (props changed)
   trunk/CHANGES
   trunk/INSTALL
   trunk/LICENSE.html
   trunk/bin/cvsdbadmin
   trunk/bin/loginfo-handler
   trunk/bin/make-database
   trunk/bin/standalone.py
   trunk/bin/svndbadmin
   trunk/docs/template-authoring-guide.html
   trunk/docs/upgrading-howto.html
   trunk/docs/url-reference.html
   trunk/lib/blame.py
   trunk/lib/config.py
   trunk/lib/cvsdb.py
   trunk/lib/debug.py
   trunk/lib/popen.py
   trunk/lib/query.py
   trunk/lib/vcauth/__init__.py
   trunk/lib/vcauth/forbidden/__init__.py
   trunk/lib/vcauth/svnauthz/__init__.py
   trunk/lib/vclib/__init__.py
   trunk/lib/vclib/ccvs/__init__.py
   trunk/lib/vclib/ccvs/blame.py
   trunk/lib/vclib/ccvs/rcsparse/common.py
   trunk/lib/vclib/ccvs/rcsparse/default.py
   trunk/lib/vclib/ccvs/rcsparse/texttools.py
   trunk/lib/vclib/svn/__init__.py
   trunk/lib/viewvc.py
   trunk/notes/releases.txt
   trunk/scripts/last-merge-tag
   trunk/templates-contrib/tabbed/templates/annotate.ezt
   trunk/templates-contrib/tabbed/templates/diff.ezt
   trunk/templates-contrib/tabbed/templates/directory.ezt
   trunk/templates-contrib/tabbed/templates/docroot/styles.css
   trunk/templates-contrib/tabbed/templates/include/diff_form.ezt
   trunk/templates-contrib/tabbed/templates/include/dir_footer.ezt
   trunk/templates-contrib/tabbed/templates/include/dir_header.ezt
   trunk/templates-contrib/tabbed/templates/include/header.ezt
   trunk/templates-contrib/tabbed/templates/include/log_footer.ezt
   trunk/templates-contrib/tabbed/templates/include/log_header.ezt
   trunk/templates-contrib/tabbed/templates/include/pathrev_form.ezt
   trunk/templates-contrib/tabbed/templates/log.ezt
   trunk/templates-contrib/tabbed/templates/markup.ezt
   trunk/templates-contrib/tabbed/templates/query_form.ezt
   trunk/templates-contrib/tabbed/templates/query_results.ezt
   trunk/templates-contrib/tabbed/templates/revision.ezt
   trunk/templates/diff.ezt
   trunk/templates/dir_new.ezt
   trunk/templates/directory.ezt
   trunk/templates/docroot/help_dirview.html
   trunk/templates/docroot/help_log.html
   trunk/templates/docroot/help_query.html
   trunk/templates/docroot/help_rootview.html
   trunk/templates/docroot/styles.css
   trunk/templates/include/diff_form.ezt
   trunk/templates/include/dir_footer.ezt
   trunk/templates/include/dir_header.ezt
   trunk/templates/include/header.ezt
   trunk/templates/include/log_header.ezt
   trunk/templates/include/paging.ezt
   trunk/templates/include/pathrev_form.ezt
   trunk/templates/include/sort.ezt
   trunk/templates/log.ezt
   trunk/templates/log_table.ezt
   trunk/templates/query_form.ezt
   trunk/templates/revision.ezt
   trunk/tparse/tparse.h
   trunk/viewvc-install
   trunk/viewvc.conf.dist
   trunk/viewvc.org/contact.html
   trunk/viewvc.org/contributing.html
   trunk/viewvc.org/download.html
   trunk/viewvc.org/faq.html
   trunk/viewvc.org/images/title.jpg
   trunk/viewvc.org/images/title.xcf
   trunk/viewvc.org/index.html
   trunk/viewvc.org/nightly/build-viewvc-snapshot
   trunk/viewvc.org/styles.css
   trunk/viewvc.org/who.html
   trunk/windows/README
   trunk/www/index.html

Modified: trunk/CHANGES
==============================================================================
--- trunk/CHANGES	(original)
+++ trunk/CHANGES	Mon Nov  3 09:57:02 2008
@@ -17,8 +17,56 @@
   * add support for disabling the checkout view (now the default state)
   * add support for ranges of revisions to svndbadmin (issue #224)
   * make the query handling more forgiving of malformatted subdirs (issue #244)
-  * add support for per-root configuration overrides
+  * add support for per-root configuration overrides (issue #371)
   * add support for optional email address mangling (issue #290)
+  * extensible path-based authorization subsystem (issue #268), supporting:
+    - Subversion authz files (new)
+    - regexp-based path hiding (for compat with 1.0.x)
+    - file glob top-level directory hiding (for compat with 1.0.x)
+  * allow default file view to be "markup" (issue #305)
+  * add support for displaying file/directory properties (issue #39)
+  * pagination improvements
+  * add gzip output encoding support for template-driven pages
+  * fix cache control bugs (issue #259)
+  * add RSS feed URL generation for file history
+  * add support for remote creation of ViewVC checkins database
+  * add integration with Pygments for syntax highlighting
+  * preserve executability of Subversion files in tarballs (issue #233)
+  * add ability to set Subversion runtime config dir (issue #351, issue #339)
+  * show RSS/query links only for roots found in commits database (issue #357)
+  * recognize Subversion svn:mime-type property values (issue #364)
+  * hide CVS files when viewing tags/branches on which they don't exist
+  * add support for hiding errorful entries from the directory view (issue #105)
+
+Version 1.0.7 (released 14-Oct-2008)
+
+  * fix regression in the 'as text' download view (issue #373)
+
+Version 1.0.6 (released 16-Sep-2008)
+
+  * security fix: ignore arbitrary user-provided MIME types (issue #354)
+  * fix bug in regexp search filter when used with sticky tag (issue #346)
+  * fix bug in handling of certain 'co' output (issue #348)
+  * fix regexp search filter template bug
+  * fix annotate code syntax error
+  * fix mod_python import cycle (issue #369)
+
+Version 1.0.5 (released 28-Feb-2008)
+
+  * security fix: omit commits of all-forbidden files from query results
+  * security fix: disallow direct URL navigation to hidden CVSROOT folder
+  * security fix: strip forbidden paths from revision view
+  * security fix: don't traverse log history thru forbidden locations
+  * security fix: honor forbiddenness via diff view path parameters
+  * new 'forbiddenre' regexp-based path authorization feature
+  * fix root name conflict resolution inconsistencies (issue #287)
+  * fix an oversight in the CVS 1.12.9 loginfo-handler support
+  * fix RSS feed content type to be more specific (issue #306)
+  * fix entity escaping problems in RSS feed data (issue #238)
+  * fix bug in tarball generation for remote Subversion repositories
+  * fix query interface file-count-limiting logic
+  * fix query results plus/minus count to ignore forbidden files
+  * fix blame error caused by 'svn' unable to create runtime config dir
 
 Version 1.0.4 (released 10-Apr-2007)
 

Modified: trunk/INSTALL
==============================================================================
--- trunk/INSTALL	(original)
+++ trunk/INSTALL	Mon Nov  3 09:57:02 2008
@@ -43,13 +43,8 @@
       * MySQL 3.22 and MySQLdb 0.9.0 or later to create a commit database
           (http://www.mysql.com/)
           (http://sourceforge.net/projects/mysql-python)
-      * one of the following syntax highlighting programs:
-          - GNU enscript
-              (http://www.codento.com/people/mtr/genscript/)
-          - Highlight 2.2.10 or later (we recommend 2.4.5 or later)
-              (http://www.andre-simon.de/)
-          - GNU source-highlight 2.5 or later
-              (http://www.gnu.org/software/src-highlite/)
+      * Pygments 0.9 or later, syntax highlighting engine
+          (http://pygments.org)
       * CvsGraph 1.5.0 or later, graphical CVS revision tree generator
           (http://www.akhphd.au.dk/~bertho/cvsgraph/)
 
@@ -225,8 +220,8 @@
    you copied the files to.
    
    Note: If you are using Mod_Python under Apache 1.3 the tarball generation
-   and enscript colorizing features may not work because they use
-   multithreading. They do work fine with Apache 2.
+   feature may not work because it uses multithreading.  This works fine
+   under Apache 2.
    
    continue with step 3).
 
@@ -256,6 +251,16 @@
       http://<server_name>/viewvc/~checkout~/<module_name>
       http://<server_name>/viewvc/<module_name>.tar.gz?view=tar
 
+5) Optional: Protect your ViewVC instance from server-whacking webcrawlers.
+
+   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
+   to suffer if a webcrawler finds your ViewVC instance and begins
+   traversing those links.  We highly recommend that you add your ViewVC
+   location to a site-wide robots.txt file.  Visit the Wikipedia page
+   for Robots.txt (http://en.wikipedia.org/wiki/Robots.txt) for more
+   information.
+
 
 UPGRADING VIEWVC
 -----------------
@@ -386,21 +391,11 @@
 ENABLING SYNTAX COLORATION
 --------------------------
 
-Enscript and Highlight are two programs that can colorize source code
-for a lot of languages.  ViewVC can be configured to use either one.
-
-1) Install Enscript, Highlight, or GNU Source Highlight using your
-   system's package manager, or by downloading from the project home pages.
-
-2) Set either the 'use_enscript' or 'use_highlight' or 'use_source_highlight'
-   option in viewvc.conf to 1.
-
-3) You may also need to set 'enscript', 'highlight', or 'source_highlight'
-   option in the [utilities] section if the executables are not located 
-   on the system PATH.
-
-That's it!  Now when you view the contents of recognized filetypes in
-ViewVC, you should see colorized syntax.
+ViewVC uses Pygments (http://pygments.org) for syntax coloration.  You
+need only install a suitable version of that module, and if ViewVC
+finds it in your Python module path, it will use it (unless you
+specifically disable the feature by setting use_pygments = 0 in your
+viewvc.conf file).
 
 
 CVSGRAPH CONFIGURATION
@@ -438,9 +433,12 @@
 Unlike the CVS integration, which simply wraps the RCS and CVS utility
 programs, the Subversion integration requires additional Python
 libraries.  To use ViewVC with Subversion, make sure you have both
-Subversion itself and the Subversion Python bindings installed.  See
-Subversion's installation notes for more details on how to build and
-install these items.
+Subversion itself and the Subversion Python bindings installed.  These
+can be obtained through typical package distribution mechanisms, or
+may be build from source.  (See the files 'INSTALL' and
+'subversion/bindings/swig/INSTALL' in the Subversion source tree for
+more details on how to build and install Subversion and its Python
+bindings.)
 
 Generally speaking, you'll know when your installation of Subversion's
 bindings has been successful if you can import the 'svn.core' module

Modified: trunk/LICENSE.html
==============================================================================
--- trunk/LICENSE.html	(original)
+++ trunk/LICENSE.html	Mon Nov  3 09:57:02 2008
@@ -15,7 +15,7 @@
 
 <blockquote>
 
-<p><strong>Copyright &copy; 1999-2007 The ViewCVS Group.  All rights
+<p><strong>Copyright &copy; 1999-2008 The ViewCVS Group.  All rights
    reserved.</strong></p>
 
 <p>By using ViewVC, you agree to the terms and conditions set forth
@@ -58,6 +58,7 @@
   <li>September 5, 2002 &mdash; copyright years updated</li>
   <li>March 17, 2006 &mdash; software renamed from "ViewCVS"</li>
   <li>April 10, 2007 &mdash; copyright years updated</li>
+  <li>February 22, 2008 &mdash; copyright years updated</li>
 </ul>
 
 </body>

Modified: trunk/bin/cvsdbadmin
==============================================================================
--- trunk/bin/cvsdbadmin	(original)
+++ trunk/bin/cvsdbadmin	Mon Nov  3 09:57:02 2008
@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 # -*-python-*-
 #
-# Copyright (C) 1999-2006 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
 # the LICENSE.html file which can be found at the top level of the ViewVC
@@ -43,7 +43,7 @@
 import string
 import cvsdb
 import viewvc
-import vclib.bincvs
+import vclib.ccvs
 
 
 def UpdateFile(db, repository, path, update, quiet_level):
@@ -129,7 +129,8 @@
        3. %s [[-q] -q] purge REPOS-PATH
 
 1.  Rebuild the commit database information for the repository located
-    at REPOS-PATH.
+    at REPOS-PATH, after first purging information specific to that
+    repository (if any).
 
 2.  Update the commit database information for all unrecorded commits
     in the repository located at REPOS-PATH.
@@ -168,6 +169,7 @@
 
     # get repository and path, and do the work
     root, path_parts = RootPath(args[2], quiet_level)
+    rootpath = vclib.ccvs.canonicalize_rootpath(root)
     try:
         cfg = viewvc.load_config(CONF_PATHNAME)
         db = cvsdb.ConnectDatabase(cfg)
@@ -178,8 +180,8 @@
             db.PurgeRepository(root)
 
         if command in ('rebuild', 'update'):
-            repository = vclib.bincvs.BinCVSRepository(None, root,
-                                                       cfg.utilities)
+            repository = vclib.ccvs.CVSRepository(None, rootpath, None,
+                                                  cfg.utilities, 0)
             RecurseUpdate(db, repository, path_parts,
                           command == 'update', quiet_level)
     except KeyboardInterrupt:

Modified: trunk/bin/loginfo-handler
==============================================================================
--- trunk/bin/loginfo-handler	(original)
+++ trunk/bin/loginfo-handler	Mon Nov  3 09:57:02 2008
@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 # -*-python-*-
 #
-# Copyright (C) 1999-2007 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
 # the LICENSE.html file which can be found at the top level of the ViewVC
@@ -44,7 +44,7 @@
 import re
 import cvsdb
 import viewvc
-import vclib.bincvs
+import vclib.ccvs
 
 DEBUG_FLAG = 0
 
@@ -223,7 +223,8 @@
 def ProcessLoginfo(rootpath, directory, files):
     cfg = viewvc.load_config(CONF_PATHNAME)
     db = cvsdb.ConnectDatabase(cfg)
-    repository = vclib.bincvs.BinCVSRepository(None, rootpath, cfg.utilities)
+    repository = vclib.ccvs.CVSRepository(None, rootpath, None,
+                                          cfg.utilities, 0)
 
     # split up the directory components
     dirpath = filter(None, string.split(os.path.normpath(directory), os.sep))

Modified: trunk/bin/make-database
==============================================================================
--- trunk/bin/make-database	(original)
+++ trunk/bin/make-database	Mon Nov  3 09:57:02 2008
@@ -20,11 +20,12 @@
 import popen2
 
 INTRO_TEXT = """\
-This script creates the database and tables in MySQL used by the ViewVC
-checkin database.  You will be prompted for: database user, database user
-password, and database name.  This script will use mysql to create the
-database for you.  You will then need to set the appropriate parameters
-in your viewvc.conf file under the [cvsdb] section.
+This script creates the database and tables in MySQL used by the
+ViewVC checkin database.  You will be prompted for:  database server
+hostname, database user, database user password, and database name.
+This script will use the 'mysql' program to create the database for
+you.  You will then need to set the appropriate parameters in the
+[cvsdb] section of your viewvc.conf file.
 """
 
 DATABASE_SCRIPT="""\
@@ -121,24 +122,27 @@
 """
 
 if __name__ == "__main__":
+  try:
     print INTRO_TEXT
-    
+
+    # Prompt for necessary information
+    host = raw_input("MySQL Hostname [default: localhost]: ") or ""
     user = raw_input("MySQL User: ")
     passwd = raw_input("MySQL Password: ")
-    dbase = raw_input("ViewVC Database Name [default: ViewVC]: ")
-    if not dbase:
-        dbase = "ViewVC"
+    dbase = raw_input("ViewVC Database Name [default: ViewVC]: ") or "ViewVC"
 
+    # Create the database
     dscript = string.replace(DATABASE_SCRIPT, "<dbname>", dbase)
-
+    host_option = host and "--host=%s" % (host) or ""
     if sys.platform == "win32":
-      # popen2.Popen3 is not provided on windows
-      cmd = "mysql --user=%s --password=%s" % (user, passwd)
-      mysql = os.popen(cmd, "w")
+      cmd = "mysql --user=%s --password=%s %s "\
+            % (user, passwd, host_option)
+      mysql = os.popen(cmd, "w") # popen2.Popen3 is not provided on windows
       mysql.write(dscript)
       status = mysql.close()
     else:
-      cmd = "{ mysql --user=%s --password=%s ; } 2>&1" % (user, passwd)
+      cmd = "{ mysql --user=%s --password=%s %s ; } 2>&1" \
+            % (user, passwd, host_option)
       pipes = popen2.Popen3(cmd)
       pipes.tochild.write(dscript)
       pipes.tochild.close()
@@ -146,9 +150,11 @@
       status = pipes.wait()
 
     if status:
-        print "[ERROR] the database did not create sucessfully."
-        sys.exit(1)
+      print "[ERROR] the database did not create sucessfully."
+      sys.exit(1)
 
     print "Database created successfully."
-    sys.exit(0)
+  except KeyboardInterrupt:
+    pass
+  sys.exit(0)
     

Modified: trunk/bin/standalone.py
==============================================================================
--- trunk/bin/standalone.py	(original)
+++ trunk/bin/standalone.py	Mon Nov  3 09:57:02 2008
@@ -17,7 +17,7 @@
 
 __author__ = "Peter Funk <pf artcom-gmbh de>"
 __date__ = "11 November 2001"
-__version__ = "$Revision: 1590 $"
+__version__ = "$Revision: 1962 $"
 __credits__ = """Guido van Rossum, for an excellent programming language.
 Greg Stein, for writing ViewCVS in the first place.
 Ka-Ping Yee, for the GUI code and the framework stolen from pydoc.py.
@@ -63,6 +63,7 @@
     else:
         host = 'localhost'
     script_alias = 'viewvc'
+    config_file = None
 
 # --- web browser interface: ----------------------------------------------
 
@@ -284,7 +285,7 @@
         # XXX Move this code out of this function.
         # Early loading of configuration here.  Used to
         # allow tinkering with some configuration settings:
-        handle_config()
+        handle_config(options.config_file)
         if options.repositories:
             cfg.general.default_root = "Development"
             for repo_name in options.repositories.keys():
@@ -331,9 +332,9 @@
         pass
     print 'server stopped'
 
-def handle_config():
+def handle_config(config_file):
   global cfg
-  cfg = viewvc.load_config(CONF_PATHNAME)
+  cfg = viewvc.load_config(config_file or CONF_PATHNAME)
 
 # --- graphical interface: --------------------------------------------------
 
@@ -576,8 +577,9 @@
     class BadUsage(Exception): pass
 
     try:
-        opts, args = getopt.getopt(argv[1:], 'gdp:r:h:s:', 
-            ['gui', 'daemon', 'port=', 'repository=', 'script-alias='])
+        opts, args = getopt.getopt(argv[1:], 'gdc:p:r:h:s:', 
+            ['gui', 'daemon', 'config-file=', 'host=',
+             'port=', 'repository=', 'script-alias='])
         for opt, val in opts:
             if opt in ('-g', '--gui'):
                 options.start_gui = 1
@@ -601,6 +603,10 @@
             elif opt in ('-s', '--script-alias'):
                 options.script_alias = \
                     string.join(filter(None, string.split(val, '/')), '/')
+            elif opt in ('-c', '--config-file'):
+                options.config_file = val
+        if options.start_gui and options.config_file:
+            raise BadUsage, "--config-file option is not valid in GUI mode."
         if not options.start_gui and not options.port:
             raise BadUsage, "You must supply a valid port, or run in GUI mode."
         if options.daemon:
@@ -608,7 +614,7 @@
             if pid != 0:
                 sys.exit()  
         if options.start_gui:
-            gui(options.host, options.port)
+            gui(options.host, options.port, options.config_file)
             return
         elif options.port:
             def ready(server):
@@ -626,10 +632,16 @@
         sys.stderr.write("""Usage: %(cmd)s [OPTIONS]
 
 Run a simple, standalone HTTP server configured to serve up ViewVC
-requests.  ViewVC configuration is read from viewvc.conf file, if available.
+requests.
 
 Options:
 
+  --config-file=PATH (-c)    Use the file at PATH as the ViewVC configuration
+                             file.  If not specified, ViewVC will try to use
+                             the configuration file in its installation tree;
+                             otherwise, built-in default values are used.
+                             (Not valid in GUI mode.)
+                             
   --daemon (-d)              Background the server process.
   
   --host=HOST (-h)           Start the server listening on HOST.  You need

Modified: trunk/bin/svndbadmin
==============================================================================
--- trunk/bin/svndbadmin	(original)
+++ trunk/bin/svndbadmin	Mon Nov  3 09:57:02 2008
@@ -66,6 +66,7 @@
 
 import cvsdb
 import viewvc
+import vclib
 
 class SvnRepo:
     """Class used to manage a connection to a SVN repository."""
@@ -278,7 +279,8 @@
        3. %s [-v] purge REPOS-PATH
 
 1.  Rebuild the commit database information for the repository located
-    at REPOS-PATH across all revisions.
+    at REPOS-PATH across all revisions, after first purging
+    information specific to that repository (if any).
 
 2.  Update the commit database information for the repository located
     at REPOS-PATH across all revisions or, optionally, only for the
@@ -324,8 +326,9 @@
 
     repository = args[2]
     if not os.path.exists(repository):
-        sys.stderr.write('ERROR: could not find repository %s\n' % repository)
+        sys.stderr.write('ERROR: could not find repository %s\n' % args[2])
         usage()
+    repository = vclib.svn.canonicalize_rootpath(repository)
 
     revs = []
     if len(sys.argv) > 3:

Modified: trunk/docs/template-authoring-guide.html
==============================================================================
--- trunk/docs/template-authoring-guide.html	(original)
+++ trunk/docs/template-authoring-guide.html	Mon Nov  3 09:57:02 2008
@@ -59,13 +59,13 @@
     <li><a href="#variables-common">Common Template Variable Set (COMMON)</a></li>
     <li><a href="#variables-pathrev">Path Revision Form Variable Set (PATHREV)</a></li>
     <li><a href="#variables-paging">Paging Form Variable Set (PAGING)</a></li>
-    <li><a href="#variables-annotate">Annotation View (annotate.ezt)</a></li>
+    <li><a href="#variables-properties">Property Listing Variable Set (PROPERTIES)</a></li>
+    <li><a href="#variables-file">File Contents View (file.ezt)</a></li>
     <li><a href="#variables-graph">Revision Graph View (graph.ezt)</a></li>
     <li><a href="#variables-diff">File Difference View (diff.ezt)</a></li>
     <li><a href="#variables-directory">Directory Listing View (directory.ezt)</a></li>
     <li><a href="#variables-error">Error View (error.ezt)</a></li>
     <li><a href="#variables-log">Revision Log View (log.ezt)</a></li>
-    <li><a href="#variables-markup">File Contents View (markup.ezt)</a></li>
     <li><a href="#variables-query_results">Revision History Query Results View (query_results.ezt, rss.ezt)</a></li>
     <li><a href="#variables-query_form">Revision History Query Form View (query_form.ezt)</a></li>
     <li><a href="#variables-revision">Revision/ChangeSet View (revision.ezt)</a></li>
@@ -113,16 +113,6 @@
       of the configuration file.</td>
 </tr>
 <tr class="varlevel1">
-  <td class="varname">change_root_action</td>
-  <td>String</td>
-  <td>Form action URL for the root selection form.</td>
-</tr>
-<tr class="varlevel1">
-  <td class="varname">change_root_hidden_values</td>
-  <td>String</td>
-  <td>Hidden value HTML markup for the root selection form.</td>
-</tr>
-<tr class="varlevel1">
   <td class="varname">docroot</td>
   <td>String</td>
   <td>URL of the static documents directory, generally used for
@@ -160,6 +150,11 @@
       abstract name is <var>l10n</var>.</td>
 </tr>
 <tr class="varlevel1">
+  <td class="varname">lockinfo</td>
+  <td>String</td>
+  <td>Information about the lock status of the current resource.</td>
+</tr>
+<tr class="varlevel1">
   <td class="varname">log_href</td>
   <td>String</td>
   <td>URL of the ViewVC revision log view for the current
@@ -223,33 +218,10 @@
   <td>Name of the current repository (root).</td>
 </tr>
 <tr class="varlevel1">
-  <td class="varname">roots</td>
-  <td>List</td>
-  <td>Set of configured viewable repositories.</td>
-</tr>
-<tr class="varlevel2">
-  <td class="varname">roots.name</td>
-  <td>String</td>
-  <td>Name of a configured repository.</td>
-</tr>
-<tr class="varlevel2">
-  <td class="varname">roots.path</td>
+  <td class="varname">roots_href</td>
   <td>String</td>
-  <td>Server-local location of a configured repository.  WARNING:  Revealing
-      information to untrusted guests about the details of your server
-      configuration can have negative security implications.  Use this
-      token at your own risk.</td>
-</tr>
-<tr class="varlevel2">
-  <td class="varname">roots.type</td>
-  <td>String</td>
-  <td>Version control type of a configured repository.  Valid
-      values: <tt>cvs</tt>, <tt>svn</tt>.</td>
-</tr>
-<tr class="varlevel2">
-  <td class="varname">roots.href</td>
-  <td>String</td>
-  <td>URL of root directory view for a configured repository.</td>
+  <td>URL of ViewVC root listing view.  Valid only when ViewVC is
+      configured in roots-as-url-components mode.</td>
 </tr>
 <tr class="varlevel1">
   <td class="varname">rootpath</td>
@@ -281,6 +253,11 @@
   <td>Link to the current object's parent directory view.</td>
 </tr>
 <tr class="varlevel1">
+  <td class="varname">username</td>
+  <td>String</td>
+  <td>Authenticated username of the requesting user.</td>
+</tr>
+<tr class="varlevel1">
   <td class="varname">view</td>
   <td>String</td>
   <td>Name of the current view.  Valid values: <tt>annotate</tt>
@@ -343,8 +320,8 @@
 </tr>
 <tr class="varlevel1">
   <td class="varname">pathrev_hidden_values</td>
-  <td>String</td>
-  <td>Hidden value HTML markup for the revision/tag selection form.</td>
+  <td>List</td>
+  <td>Hidden value name/value pairs for the revision/tag selection form.</td>
 </tr>
 <tr class="varlevel1">
   <td class="varname">pathrev_clear_action</td>
@@ -353,8 +330,8 @@
 </tr>
 <tr class="varlevel1">
   <td class="varname">pathrev_clear_hidden_values</td>
-  <td>String</td>
-  <td>Hidden value HTML markup for the path revision clear button.</td>
+  <td>List</td>
+  <td>Hidden value name/value pairs for the path revision clear button.</td>
 </tr>
 </tbody>
 </table>
@@ -377,9 +354,9 @@
   <td>List of pages that make up the current directory or log view.</td>
 </tr>
 <tr class="varlevel2">
-  <td class="varname">picklist.start</td>
+  <td class="varname">picklist.count</td>
   <td>String</td>
-  <td>Name of first item on the page</td>
+  <td>Number of the first item on the page (indexed from 0)</td>
 </tr>
 <tr class="varlevel2">
   <td class="varname">picklist.end</td>
@@ -387,14 +364,21 @@
   <td>Name of last item on the page</td>
 </tr>
 <tr class="varlevel2">
+  <td class="varname">picklist.more</td>
+  <td>Boolean</td>
+  <td>If set, indicates that this picklist item is a placeholder for
+      an unspecified number of additional pages.  In this case,
+      <code>picklist.end</code> is undefined.</td>
+</tr>
+<tr class="varlevel2">
   <td class="varname">picklist.page</td>
   <td>String</td>
   <td>Page number (indexed from 1)</td>
 </tr>
 <tr class="varlevel2">
-  <td class="varname">picklist.count</td>
+  <td class="varname">picklist.start</td>
   <td>String</td>
-  <td>Number of the first item on the page (indexed from 0)</td>
+  <td>Name of first item on the page</td>
 </tr>
 <tr class="varlevel1">
   <td class="varname">picklist_len</td>
@@ -406,7 +390,44 @@
 </div>
 
 <div class="h3">
-<h3 id="variables-annotate">Annotation View (annotate.ezt)</h3>
+<h3 id="variables-properties">Property Listing Variable Set (PROPERTIES)</h3>
+<table>
+<thead>
+<tr>
+  <th>Variable</th>
+  <th>Type</th>
+  <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr class="varlevel1">
+  <td class="varname">properties</td>
+  <td>List</td>
+  <td>List of item properties set on the current directory, minus
+      those with undisplayable names.</td>
+</tr>
+<tr class="varlevel2">
+  <td class="varname">properties.name</td>
+  <td>String</td>
+  <td>Name of an item property.</td>
+</tr>
+<tr class="varlevel2">
+  <td class="varname">properties.undisplayable</td>
+  <td>Boolean</td>
+  <td>Indicates whether or not the value of this property is
+      undisplayable (by virtue of not being UTF-8 text).</td>
+</tr>
+<tr class="varlevel2">
+  <td class="varname">properties.value</td>
+  <td>String</td>
+  <td>Value of this property.</td>
+</tr>
+</tbody>
+</table>
+</div>
+
+<div class="h3">
+<h3 id="variables-file">File Contents View (file.ezt)</h3>
 <table>
 <thead>
 <tr>
@@ -418,7 +439,8 @@
 <tbody>
 <tr class="include">
   <td colspan="3">Includes all variables from the 
-     <a href="#variables-common">COMMON</a> variable set</td>
+     <a href="#variables-common">COMMON</a> and
+     <a href="#variables-properties">PROPERTIES</a> variable sets</td>
 </tr>
 <tr class="varlevel1">
   <td class="varname">ago</td>
@@ -426,6 +448,14 @@
   <td>Text description of the time elapsed since <var>date</var>.</td>
 </tr>
 <tr class="varlevel1">
+  <td class="varname">annotation</td>
+  <td>String</td>
+  <td>If set, indicates that annotations were requested.  Valid values
+      are "annotated" (annotation was successful), "binary" (file contents
+      are not line-based and human-readable), and "error" (something went
+      wrong during annotation).</td>
+</tr>
+<tr class="varlevel1">
   <td class="varname">author</td>
   <td>String</td>
   <td>Author of the revision being viewed.</td>
@@ -454,6 +484,13 @@
       being viewed.</td>
 </tr>
 <tr class="varlevel1">
+  <td class="varname">image_src_href</td>
+  <td>String</td>
+  <td>URL used to display the current revision of the file as an
+      embedded image.  (Set only if the file is not a web-viewable
+      image.)</td>
+</tr>
+<tr class="varlevel1">
   <td class="varname">lines</td>
   <td>List</td>
   <td>Set of objects containing information about the most recent
@@ -473,7 +510,7 @@
       the line.</td>
 </tr>
 <tr class="varlevel2">
-  <td class="varname">lines.diff_url</td>
+  <td class="varname">lines.diff_href</td>
   <td>String</td>
   <td>URL of the ViewVC file difference view which displays the
       modification of the line.</td>
@@ -687,8 +724,8 @@
 </tr>
 <tr class="varlevel1">
   <td class="varname">diff_format_hidden_values</td>
-  <td>String</td>
-  <td>Hidden value HTML markup for the diff format selection form.</td>
+  <td>List</td>
+  <td>Hidden value name/value pairs for the diff format selection form.</td>
 </tr>
 <tr class="varlevel1">
   <td class="varname">left</td>
@@ -836,8 +873,9 @@
 <tr class="include">
   <td colspan="3">Includes all variables from the 
      <a href="#variables-common">COMMON</a>,
-     <a href="#variables-pathrev">PATHREV</a>, and 
-     <a href="#variables-paging">PAGING</a> variable sets</td>
+     <a href="#variables-pathrev">PATHREV</a>,
+     <a href="#variables-paging">PAGING</a>, and
+     <a href="#variables-properties">PROPERTIES</a> variable sets</td>
 </tr>
 <tr class="varlevel1">
   <td class="varname">attic_showing</td>
@@ -866,8 +904,8 @@
 </tr>
 <tr class="varlevel1">
   <td class="varname">dir_paging_hidden_values</td>
-  <td>String</td>
-  <td>Hidden value HTML markup for the page selection form.</td>
+  <td>List</td>
+  <td>Hidden value name/value pairs for the page selection form.</td>
 </tr>
 <tr class="varlevel1">
   <td class="varname">entries</td>
@@ -923,6 +961,11 @@
       entry.</td>
 </tr>
 <tr class="varlevel2">
+  <td class="varname">entries.lockinfo</td>
+  <td>String</td>
+  <td>Information about the lock status of the directory entry.</td>
+</tr>
+<tr class="varlevel2">
   <td class="varname">entries.log</td>
   <td>String</td>
   <td>Log message of last modification to the directory entry.</td>
@@ -1059,8 +1102,8 @@
 </tr>
 <tr class="varlevel1">
   <td class="varname">search_re_hidden_values</td>
-  <td>String</td>
-  <td>Hidden value HTML markup for the regular expression search form.</td>
+  <td>List</td>
+  <td>Hidden value name/value pairs for the regular expression search form.</td>
 </tr>
 <tr class="varlevel1">
   <td class="varname">show_attic_href</td>
@@ -1181,11 +1224,6 @@
      <a href="#variables-paging">PAGING</a> variable sets</td>
 </tr>
 <tr class="varlevel1">
-  <td class="varname">annotate_href</td>
-  <td>String</td>
-  <td>URL for annotate view of the HEAD revision of the file.</td>
-</tr>
-<tr class="varlevel1">
   <td class="varname">branch_tags</td>
   <td>List</td>
   <td>Names of branch tags in the file. CVS only.</td>
@@ -1210,19 +1248,8 @@
 </tr>
 <tr class="varlevel1">
   <td class="varname">diff_select_hidden_values</td>
-  <td>String</td>
-  <td>Hidden value HTML markup for the diff selection form.</td>
-</tr>
-<tr class="varlevel1">
-  <td class="varname">download_href</td>
-  <td>String</td>
-  <td>URL to download the HEAD revision of the file.</td>
-</tr>
-<tr class="varlevel1">
-  <td class="varname">download_text_href</td>
-  <td>String</td>
-  <td>URL to download the HEAD revision of the file as
-      <tt>text/plain</tt>.</td>
+  <td>List</td>
+  <td>Hidden value name/value pairs for the diff selection form.</td>
 </tr>
 <tr class="varlevel1">
   <td class="varname">entries</td>
@@ -1357,6 +1384,11 @@
   <td>URL to download the file revision as <tt>text/plain</tt>.</td>
 </tr>
 <tr class="varlevel2">
+  <td class="varname">entries.lockinfo</td>
+  <td>String</td>
+  <td>Information about the lock status of this revision.</td>
+</tr>
+<tr class="varlevel2">
   <td class="varname">entries.log</td>
   <td>String</td>
   <td>Revision log message.</td>
@@ -1451,6 +1483,33 @@
       for a directory revision.</td>
 </tr>
 <tr class="varlevel1">
+  <td class="varname">head_annotate_href</td>
+  <td>String</td>
+  <td>URL for annotate view of the HEAD revision of the file.</td>
+</tr>
+<tr class="varlevel1">
+  <td class="varname">head_download_href</td>
+  <td>String</td>
+  <td>URL to download the HEAD revision of the file.</td>
+</tr>
+<tr class="varlevel1">
+  <td class="varname">head_download_text_href</td>
+  <td>String</td>
+  <td>URL to download the HEAD revision of the file as
+      <tt>text/plain</tt>.</td>
+</tr>
+<tr class="varlevel1">
+  <td class="varname">head_prefer_markup</td>
+  <td>Boolean</td>
+  <td>Indicates whether to make the default HEAD file link a link to the markup
+      page instead of the checkout page.</td>
+</tr>
+<tr class="varlevel1">
+  <td class="varname">head_view_href</td>
+  <td>String</td>
+  <td>URL for markup view of the HEAD revision of the file.</td>
+</tr>
+<tr class="varlevel1">
   <td class="varname">human_readable</td>
   <td>Boolean</td>
   <td>Indicates whether or not currently selected diff format
@@ -1470,8 +1529,8 @@
 </tr>
 <tr class="varlevel1">
   <td class="varname">log_paging_hidden_values</td>
-  <td>String</td>
-  <td>Hidden value HTML markup for the page selection form.</td>
+  <td>List</td>
+  <td>Hidden value name/value pairs for the page selection form.</td>
 </tr>
 <tr class="varlevel1">
   <td class="varname">logsort</td>
@@ -1486,8 +1545,8 @@
 </tr>
 <tr class="varlevel1">
   <td class="varname">logsort_hidden_values</td>
-  <td>String</td>
-  <td>Hidden value HTML markup for the log sort drop down box</td>
+  <td>List</td>
+  <td>Hidden value name/value pairs for the log sort drop down box</td>
 </tr>
 <tr class="varlevel1">
   <td class="varname">mime_type</td>
@@ -1500,12 +1559,6 @@
   <td>Names of non-branch in the file. CVS only.</td>
 </tr>
 <tr class="varlevel1">
-  <td class="varname">prefer_markup</td>
-  <td>Boolean</td>
-  <td>Indicates whether to make the default file link a link to the markup
-      page instead of the checkout page.</td>
-</tr>
-<tr class="varlevel1">
   <td class="varname">rev_selected</td>
   <td>String</td>
   <td>Revision number currently selected for diffs.</td>
@@ -1531,8 +1584,8 @@
 <tr class="varlevel1">
   <td class="varname">tag_prefer_markup</td>
   <td>Boolean</td>
-  <td>Indicates whether to make the default file link a link to the markup
-      page instead of the checkout page.</td>
+  <td>Indicates whether to make the default sticky tag file link a
+      link to the markup page instead of the checkout page.</td>
 </tr>
 <tr class="varlevel1">
   <td class="varname">tag_view_href</td>
@@ -1556,117 +1609,6 @@
   <td>String</td>
   <td>Tag name</td>
 </tr>
-<tr class="varlevel1">
-  <td class="varname">view_href</td>
-  <td>String</td>
-  <td>URL for markup view of the HEAD revision of the file.</td>
-</tr>
-</tbody>
-</table>
-</div>
-
-<div class="h3">
-<h3 id="variables-markup">File Contents View (markup.ezt)</h3>
-<table>
-<thead>
-<tr>
-  <th>Variable</th>
-  <th>Type</th>
-  <th>Description</th>
-</tr>
-</thead>
-<tbody>
-<tr class="include">
-  <td colspan="3">Includes all variables from the 
-     <a href="#variables-common">COMMON</a> variable set</td>
-</tr>
-<tr class="varlevel1">
-  <td class="varname">ago</td>
-  <td>String</td>
-  <td>Text description of the time elapsed since <var>date</var>.</td>
-</tr>
-<tr class="varlevel1">
-  <td class="varname">author</td>
-  <td>String</td>
-  <td>Author of the revision being viewed.</td>
-</tr>
-<tr class="varlevel1">
-  <td class="varname">branch_points</td>
-  <td>String</td>
-  <td>List of branch tag names which branch off of the revision being
-      viewed (CVS only).</td>
-</tr>
-<tr class="varlevel1">
-  <td class="varname">branches</td>
-  <td>List</td>
-  <td>If revision currently being viewed is on a branch, list of names
-      for the branch.</td>
-</tr>
-<tr class="varlevel1">
-  <td class="varname">changed</td>
-  <td>String</td>
-  <td>Numbers of lines added and removed since the previous revision.</td>
-</tr>
-<tr class="varlevel1">
-  <td class="varname">date</td>
-  <td>String</td>
-  <td>Date (in UTC if not otherwise configured) of the revision currently
-      being viewed.</td>
-</tr>
-<tr class="varlevel1">
-  <td class="varname">log</td>
-  <td>String</td>
-  <td>Log message of the revision currently being viewed.</td>
-</tr>
-<tr class="varlevel1">
-  <td class="varname">markup</td>
-  <td>File</td>
-  <td>Marked up contents of the current file revision.</td>
-</tr>
-<tr class="varlevel1">
-  <td class="varname">mime_type</td>
-  <td>String</td>
-  <td>MIME type of the current file.</td>
-</tr>
-<tr class="varlevel1">
-  <td class="varname">orig_path</td>
-  <td>String</td>
-  <td>When viewing an old file revision through a copy of the file,
-      this is the old file revision's original path.</td>
-</tr>
-<tr class="varlevel1">
-  <td class="varname">orig_href</td>
-  <td>String</td>
-  <td>URL of a ViewVC log view for <code>orig_path</code>.</td>
-</tr>
-<tr class="varlevel1">
-  <td class="varname">prev</td>
-  <td>String</td>
-  <td>Previous revision number.</td>
-</tr>
-<tr class="varlevel1">
-  <td class="varname">size</td>
-  <td>String</td>
-  <td>Size of the file revision, in bytes. Subversion only.</td>
-</tr>
-<tr class="varlevel1">
-  <td class="varname">state</td>
-  <td>String</td>
-  <td>State of the file revision. Possible values: <tt>dead</tt>, and
-      the empty string.</td>
-</tr>
-<tr class="varlevel1">
-  <td class="varname">tags</td>
-  <td>List</td>
-  <td>Names of tags that have been applied to the current file
-      revision.</td>
-</tr>
-<tr class="varlevel1">
-  <td class="varname">vendor_branch</td>
-  <td>Boolean</td>
-  <td>Indicates whether or not the current file revision is on a vendor
-      branch.</td>
-</tr>
 </tbody>
 </table>
 </div>
@@ -1784,9 +1726,19 @@
   <td>True if files list was cut short due to <tt>limit_changes</tt>.</td>
 </tr>
 <tr class="varlevel2">
+  <td class="varname">commits.minus</td>
+  <td>String</td>
+  <td>Total number of lines removed from files in this commit.</td>
+</tr>
+<tr class="varlevel2">
   <td class="varname">commits.num_files</td>
   <td>String</td>
-  <td>Number of files in the <var>commits.files</var> list.</td>
+  <td>Total number of files in the <var>commits.files</var> list.</td>
+</tr>
+<tr class="varlevel2">
+  <td class="varname">commits.plus</td>
+  <td>String</td>
+  <td>Number of lines added to files in this commit.</td>
 </tr>
 <tr class="varlevel2">
   <td class="varname">commits.rev</td>
@@ -1827,12 +1779,14 @@
 <tr class="varlevel1">
   <td class="varname">minus_count</td>
   <td>String</td>
-  <td>Total number of lines removed in the commit (over all files).</td>
+  <td>Total number of lines removed from all files across all returned
+      commits.</td>
 </tr>
 <tr class="varlevel1">
   <td class="varname">plus_count</td>
   <td>String</td>
-  <td>Total number of lines added in the commit (over all files).</td>
+  <td>Total number of lines added to all files across all returned
+      commits.</td>
 </tr>
 <tr class="varlevel1">
   <td class="varname">querysort</td>
@@ -1955,8 +1909,8 @@
 </tr>
 <tr class="varlevel1">
   <td class="varname">query_hidden_values</td>
-  <td>String</td>
-  <td>Hidden value HTML markup for query form.</td>
+  <td>List</td>
+  <td>Hidden value name/value pairs for query form.</td>
 </tr>
 <tr class="varlevel1">
   <td class="varname">querysort</td>
@@ -2095,8 +2049,8 @@
 </tr>
 <tr class="varlevel1">
   <td class="varname">jump_rev_hidden_values</td>
-  <td>String</td>
-  <td>Hidden value HTML markup for revision jump form.</td>
+  <td>List</td>
+  <td>Hidden value name/value pairs for revision jump form.</td>
 </tr>
 <tr class="varlevel1">
   <td class="varname">limit_changes</td>
@@ -2153,6 +2107,35 @@
   <td colspan="3">Includes all variables from the 
      <a href="#variables-common">COMMON</a> variable set</td>
 </tr>
+<tr class="varlevel1">
+  <td class="varname">roots</td>
+  <td>List</td>
+  <td>Set of configured viewable repositories.</td>
+</tr>
+<tr class="varlevel2">
+  <td class="varname">roots.name</td>
+  <td>String</td>
+  <td>Name of a configured repository.</td>
+</tr>
+<tr class="varlevel2">
+  <td class="varname">roots.path</td>
+  <td>String</td>
+  <td>Server-local location of a configured repository.  WARNING:  Revealing
+      information to untrusted guests about the details of your server
+      configuration can have negative security implications.  Use this
+      token at your own risk.</td>
+</tr>
+<tr class="varlevel2">
+  <td class="varname">roots.type</td>
+  <td>String</td>
+  <td>Version control type of a configured repository.  Valid
+      values: <tt>cvs</tt>, <tt>svn</tt>.</td>
+</tr>
+<tr class="varlevel2">
+  <td class="varname">roots.href</td>
+  <td>String</td>
+  <td>URL of root directory view for a configured repository.</td>
+</tr>
 </tbody>
 </table>
 

Modified: trunk/docs/upgrading-howto.html
==============================================================================
--- trunk/docs/upgrading-howto.html	(original)
+++ trunk/docs/upgrading-howto.html	Mon Nov  3 09:57:02 2008
@@ -109,14 +109,117 @@
 </div>
 
 <div class="h3">
-<h3>Mod_Python Stubs</h3>
+<h3>Path-Based Authorization / Forbidden Modules</h3>
     
-<p>The Mod_Python stub scripts haved been renamed from
-   <code>viewvc.py</code> and <code>query.py</code> to
-   <code>viewvc_mp.py</code> and <code>query_mp.py</code>,
-   respectively, to avoid triggering an Exception from Mod_Python's
-   module import loop detection logic.  You'll need to update your
-   Apache configuration files to point to the new script names.</p>
+<p>ViewVC 1.1 introduces a new pluggable authorization (authz)
+   subsystem which gives administrators greater control over the
+   accessibility of the information ViewVC displays in its output.
+   ViewVC provides a number of working authz modules and a framework for
+   configuring them.  But of specific interest to folks upgrading from
+   ViewVC 1.0 is that one of these new modules has replaced the
+   handling of forbidden modules.  As such, the <code>forbidden</code>
+   configuration option now lives under the configuration section
+   specific to that authz module.</p>
+
+<p>Migrating your existing configuration of forbidden modules should
+   be fairly straightforward:</p>
+
+<ol>
+
+   <li>In the new "authz-forbidden" section of viewvc.conf, set the
+       <code>forbidden</code> option to the same value as the
+       <code>forbidden</code> option in your ViewVC 1.0.x
+       configuration's "general" section.</li>
+
+   <li>In the new "authz-forbiddenre" section of viewvc.conf, set the
+       <code>forbiddenre</code> option to the same value as the
+       <code>forbiddenre</code> option in your ViewVC 1.0.x
+       configuration's "general" section.</li>
+
+   <li>Finally, ensure that that the new <code>authorizer</code>
+       option is set to either "forbidden" (which is the default) or
+       "forbiddenre", depending on which of those you were using in
+       ViewVC 1.0.x.</li>
+
+</ol>
+
+<p>Of course, you might wish to take advantage of another of the
+   provided authz modules.  Or, you might wish to write a brand new
+   one for your purposes.  The flexibility is yours.</p>
+
+<p><strong>Known Issues:</strong></p>
+
+<ul>
+
+   <li>ViewVC does not provide an <em>authentication</em> framework.
+       It does, however, inherit authenticated usernames as determined
+       by the HTTP server (Apache, e.g.) via the CGI environment.  So,
+       any authorization module that assigns privileges based on
+       usernames will work only if ViewVC is deployed in a way that
+       requires successful authentication (as opposed to allowing
+       anonymous access).</li>
+
+   <li>Currently, the root listing view only honors the global or
+       vhost-specific configurations, <em>not</em> any root-specific
+       configuration.  In the event that ViewVC is using root-specific
+       configuration for its authorization stuffs, this may cause
+       either the unintended leak of root names to users or the
+       inability to see roots at all.  However, for root-specific
+       ViewVC views, all configuration &mdash; include root-specific
+       configuration &mdash; is honored.  If you are concerned about
+       leaking root names in the root listing view, you might consider
+       disabling that view altogether by removing <code>roots</code>
+       from the list of views specified in the 
+       <code>allowed_views</code> configuration option.</li>
+
+   <li>The experimental module which allows ViewVC to serve up views
+       of remote Subversion repositories is not yet fully integrated
+       with the authorization subsystem, and almost certainly will
+       leak privileged data.  Sorry.  That's (one reason) why it's
+       experimental.</li>
+
+</ul>
+
+</div>
+
+<div class="h3">
+<h3>Syntax Highlighting</h3>
+
+<p>ViewVC 1.0.x supports syntax highlighting provided by multiple
+   third-party highlighting engines, including GNU enscript, GNU
+   source-highlight, highlight, php, and py2html.  Unfortunately, each
+   of those integrations worked differently than the others.  Some
+   supported line numbers, some didn't; some were under active
+   development and recognized newer languages; some weren't; each had
+   its own set of CSS stylations that needed to be customized;
+   etc.</p>
+
+<p>In ViewVC 1.1, we've dropped support for all of those integations
+   in favor of a single integration with <a
+   href="http://www.pygments.org/"; >Pygments</a>, a
+   Python-module-based syntax highlighting engine.  As such, the
+   configuration options for the various other engines (both those
+   that enabled the integration and those that specified the locations
+   of the third-party tools) have been removed from ViewVC, and have
+   been replaced by a single new configuration option:
+   <code>enable_syntax_coloration</code>.</p>
+
+<p>The list of removed options is as follows:</p>
+
+<ul>
+  <li>options/enscript_path</li>
+  <li>options/highlight_convert_tabs</li>
+  <li>options/highlight_path</li>
+  <li>options/markup_line_numbers</li>
+  <li>options/php_exe</li>
+  <li>options/py2html_path</li>
+  <li>options/use_enscript</li>
+  <li>options/use_highlight</li>
+  <li>options/use_php</li>
+  <li>options/use_py2html</li>
+  <li>options/use_pygments</li>
+  <li>options/use_source_highlight</li>
+</ul>
 
 </div>
 
@@ -149,32 +252,6 @@
 </div>
 
 <div class="h3">
-<h3>Forbidden Modules</h3>
-    
-<p>ViewVC 1.1 introduces a new pluggable authorization (authz)
-   subsystem which gives administrators greater control over the
-   accessibility of the information ViewVC displays in its output.
-   ViewVC provides a number of working authz modules and framework for
-   configuring them.  But of specific interest to folks upgrading from
-   ViewVC 1.0 is that one of these new modules has replaced the
-   handling of forbidden modules.  As such, the <code>forbidden</code>
-   configuration option now lives under the configuration section
-   specific to that authz module.</p>
-
-<p>Migrating your existing configuration of forbidden modules should
-   be fairly straightforward.  Simply ensure that that the new
-   <code>authorizer</code> option is set to "forbidden" (which is the
-   default), and in the new "authz-forbidden" section of viewvc.conf,
-   set the <code>forbidden</code> option to the same thing it was set
-   to when it lived in the "options" section.</p>
-
-<p>Of course, you might wish to take advantage of another of the
-   provided authz modules.  Or, you might wish to write a brand new
-   one for your purposes.  The flexibility is yours.</p>
-
-</div>
-
-<div class="h3">
 <h3>Configuration Options</h3>
 
 <p>This section covers changes to configuration options not already
@@ -188,17 +265,10 @@
    following options were added:</p>
 
 <ul>
-  <li>options/use_py2html</li>
   <li>utilities/cvsgraph</li>
   <li>utilities/cvsnt</li>
   <li>utilities/diff</li>
-  <li>utilities/enscript</li>
-  <li>utilities/gzip</li>
-  <li>utilities/highlight</li>
-  <li>utilities/php</li>
-  <li>utilities/py2html_dir</li>
   <li>utilities/rcs_dir</li>
-  <li>utilities/sed</li>
   <li>utilities/svn</li>
 </ul>
 
@@ -209,10 +279,6 @@
   <li>general/rcs_path</li>
   <li>general/svn_path</li>
   <li>options/cvsgraph_path</li>
-  <li>options/enscript_path</li>
-  <li>options/highlight_path</li>
-  <li>options/php_exe</li>
-  <li>options/py2html_path</li>
 </ul>
 
 <p>All the options which governed which ViewVC views were enabled have
@@ -233,6 +299,10 @@
 <p>The <code>use_rcsparse</code> option was moved from the "general"
    section to the "options" section.</p>
 
+<p>The <code>log_sort</code> option's value "cvs" has been renamed to
+   "none" (for general application across all supported version
+   control systems).</p>
+
 <p>Custom sections which define per-virtual-host configuration option
    overrides must now have their names prefixed with "vhost-".  Also,
    instead of a hyphen (-) between the virtual host name and the base
@@ -261,6 +331,7 @@
 <p>The following is a grab-bag of additional new options:</p>
 
 <ul>
+  <li>options/hide_errorful_entries</li>
   <li>options/mangle_email_addresses</li>
 </ul>
 
@@ -274,6 +345,16 @@
    Authoring Guide</a> for the current set of variables available to
    each templates.</p>
 
+<p>One notable change in ViewVC 1.1 is that the markup.ezt and
+   annotate.ezt templates have been combined into a single file.ezt
+   template.</p>
+
+<p>Also, the configuration options under the <code>[templates]</code>
+   section are now paths relative to the configured template directory
+   (either the value of the <code>options/template_dir</code>
+   option, or the default "templates" directory), instead of being
+   relative to the configuration location.</p>
+
 <table>
 <thead>
 <tr>
@@ -288,108 +369,68 @@
   <td><em>all templates</em></td>
   <td>added</td>
 </tr>
+<tr class="removed">
+  <td class="varname">change_root_action</td>
+  <td><em>all templates</em></td>
+  <td>removed</td>
+</tr>
+<tr class="removed">
+  <td class="varname">change_root_hidden_values</td>
+  <td><em>all templates</em></td>
+  <td>removed</td>
+</tr>
+<tr class="removed">
+  <td class="varname">roots</td>
+  <td><em>all templates</em> except roots.ezt</td>
+  <td>removed</td>
+</tr>
 <tr class="added">
   <td class="varname">roots.path</td>
-  <td><em>all templates</em></td>
+  <td>roots.ezt</td>
   <td>added</td>
 </tr>
 <tr class="added">
   <td class="varname">queryform_href</td>
-  <td>annotate.ezt, diff.ezt, graph.ezt, log.ezt, log_table.ezt, markup.ezt, 
+  <td>diff.ezt, file.ezt, graph.ezt, log.ezt, log_table.ezt, 
       query_form.ezt, revision.ezt, roots.ezt</td>
   <td>added</td>
 </tr>
 <tr class="added">
   <td class="varname">tarball_href</td>
-  <td>annotate.ezt, diff.ezt, graph.ezt, log.ezt, log_table.ezt, markup.ezt, 
+  <td>diff.ezt, file.ezt, graph.ezt, log.ezt, log_table.ezt,
       query_form.ezt, query_results.ezt, revision.ezt, roots.ezt</td>
   <td>added</td>
 </tr>
 <tr class="added">
-  <td class="varname">mime_type</td>
-  <td>annotate.ezt</td>
+  <td class="varname">properties</td>
+  <td>directory.ezt, file.ezt</td>
   <td>added</td>
 </tr>
 <tr class="added">
-  <td class="varname">log</td>
-  <td>annotate.ezt</td>
+  <td class="varname">properties.name</td>
+  <td>directory.ezt, file.ezt</td>
   <td>added</td>
 </tr>
 <tr class="added">
-  <td class="varname">date</td>
-  <td>annotate.ezt</td>
-  <td>added</td>
-</tr>
-<tr class="added">
-  <td class="varname">ago</td>
-  <td>annotate.ezt</td>
+  <td class="varname">properties.undisplayable</td>
+  <td>directory.ezt, file.ezt</td>
   <td>added</td>
 </tr>
 <tr class="added">
-  <td class="varname">author</td>
-  <td>annotate.ezt</td>
+  <td class="varname">properties.value</td>
+  <td>directory.ezt, file.ezt</td>
   <td>added</td>
 </tr>
-<tr class="added">
-  <td class="varname">branches</td>
-  <td>annotate.ezt</td>
-  <td>added</td>
-</tr>
-<tr class="added">
-  <td class="varname">tags</td>
-  <td>annotate.ezt</td>
-  <td>added</td>
-</tr>
-<tr class="added">
-  <td class="varname">branch_points</td>
-  <td>annotate.ezt</td>
-  <td>added</td>
-</tr>
-<tr class="added">
-  <td class="varname">changed</td>
-  <td>annotate.ezt</td>
-  <td>added</td>
-</tr>
-<tr class="added">
-  <td class="varname">size</td>
-  <td>annotate.ezt</td>
-  <td>added</td>
-</tr>
-<tr class="added">
-  <td class="varname">state</td>
-  <td>annotate.ezt</td>
-  <td>added</td>
-</tr>
-<tr class="added">
-  <td class="varname">vendor_branch</td>
-  <td>annotate.ezt</td>
-  <td>added</td>
-</tr>
-<tr class="added">
-  <td class="varname">prev</td>
-  <td>annotate.ezt</td>
-  <td>added</td>
+<tr class="changed">
+  <td class="varname">diff_format_hidden_values</td>
+  <td>diff.ezt</td>
+  <td>now is an iterable list of objects with .name and .value attributes</td>
 </tr>
 <tr class="changed">
   <td class="varname">diff_type</td>
   <td>diff.ezt</td>
   <td>new value: <code>f</code></td>
 </tr>
-<tr class="changed">
-  <td class="varname">entries.log</td>
-  <td>directory.ezt, dir_alternate.ezt</td>
-  <td>now always contains untruncated log message</td>
-</tr>
-<tr class="added">
-  <td class="varname">entries.short_log</td>
-  <td>directory.ezt, dir_alternate.ezt</td>
-  <td>added</td>
-</tr>
-<tr class="added">
-  <td class="varname">rss_link_href</td>
-  <td>query.ezt, rss.ezt</td>
-  <td>added</td>
-</tr>
 <tr class="renamed">
   <td class="varname">date_left</td>
   <td>diff.ezt</td>
@@ -500,6 +541,96 @@
   <td>diff.ezt</td>
   <td>added</td>
 </tr>
+<tr class="changed">
+  <td class="varname">dir_paging_hidden_values</td>
+  <td>directory.ezt, , dir_alternate.ezt</td>
+  <td>now is an iterable list of objects with .name and .value attributes</td>
+</tr>
+<tr class="changed">
+  <td class="varname">entries.log</td>
+  <td>directory.ezt, dir_alternate.ezt</td>
+  <td>now always contains untruncated log message</td>
+</tr>
+<tr class="added">
+  <td class="varname">entries.short_log</td>
+  <td>directory.ezt, dir_alternate.ezt</td>
+  <td>added</td>
+</tr>
+<tr class="changed">
+  <td class="varname">search_re_hidden_values</td>
+  <td>directory.ezt, dir_alternate.ezt</td>
+  <td>now is an iterable list of objects with .name and .value attributes</td>
+</tr>
+<tr class="changed">
+  <td class="varname">search_tag_hidden_values</td>
+  <td>directory.ezt, dir_alternate.ezt</td>
+  <td>now is an iterable list of objects with .name and .value attributes</td>
+</tr>
+<tr class="changed">
+  <td class="varname">pathrev_clear_hidden_values</td>
+  <td>log.ezt, log_table.ezt, directory.ezt, dir_alternate.ezt</td>
+  <td>now is an iterable list of objects with .name and .value attributes</td>
+</tr>
+<tr class="changed">
+  <td class="varname">pathrev_hidden_values</td>
+  <td>log.ezt, log_table.ezt, directory.ezt, dir_alternate.ezt</td>
+  <td>now is an iterable list of objects with .name and .value attributes</td>
+</tr>
+<tr class="renamed">
+  <td class="varname">annotate_href</td>
+  <td>log.ezt, log_table.ezt</td>
+  <td>renamed to <code>head_annotate_href</code></td>
+</tr>
+<tr class="changed">
+  <td class="varname">diff_form_hidden_values</td>
+  <td>log.ezt, log_table.ezt</td>
+  <td>now is an iterable list of objects with .name and .value attributes</td>
+</tr>
+<tr class="renamed">
+  <td class="varname">download_href</td>
+  <td>log.ezt, log_table.ezt</td>
+  <td>renamed to <code>head_download_href</code></td>
+</tr>
+<tr class="renamed">
+  <td class="varname">download_text_href</td>
+  <td>log.ezt, log_table.ezt</td>
+  <td>renamed to <code>head_download_text_href</code></td>
+</tr>
+<tr class="changed">
+  <td class="varname">log_paging_hidden_values</td>
+  <td>log.ezt, log_table.ezt</td>
+  <td>now is an iterable list of objects with .name and .value attributes</td>
+</tr>
+<tr class="changed">
+  <td class="varname">logsort_hidden_values</td>
+  <td>log.ezt, log_table.ezt</td>
+  <td>now is an iterable list of objects with .name and .value attributes</td>
+</tr>
+<tr class="renamed">
+  <td class="varname">prefer_markup</td>
+  <td>log.ezt, log_table.ezt</td>
+  <td>renamed to <code>head_prefer_markup</code></td>
+</tr>
+<tr class="renamed">
+  <td class="varname">view_href</td>
+  <td>log.ezt, log_table.ezt</td>
+  <td>renamed to <code>head_view_href</code></td>
+</tr>
+<tr class="added">
+  <td class="varname">rss_link_href</td>
+  <td>query.ezt, rss.ezt</td>
+  <td>added</td>
+</tr>
+<tr class="changed">
+  <td class="varname">query_hidden_values</td>
+  <td>query_form.ezt</td>
+  <td>now is an iterable list of objects with .name and .value attributes</td>
+</tr>
+<tr class="changed">
+  <td class="varname">jump_rev_hidden_values</td>
+  <td>revision.ezt</td>
+  <td>now is an iterable list of objects with .name and .value attributes</td>
+</tr>
 </tbody>
 </table>
 

Modified: trunk/docs/url-reference.html
==============================================================================
--- trunk/docs/url-reference.html	(original)
+++ trunk/docs/url-reference.html	Mon Nov  3 09:57:02 2008
@@ -1,6 +1,6 @@
 <html>
 <head>
-<title>ViewVC 1.0 URL Reference</title>
+<title>ViewVC 1.1 URL Reference</title>
 <style>
 body {
   background-color: rgb(180,193,205);
@@ -88,7 +88,7 @@
   <ul>
     <li><a href="#compat-viewrev">'<code>view=rev</code>' Parameter &rArr; '<code>view=revision</code>'</a></li>
     <li><a href="#compat-cvsroot">'<code>cvsroot</code>' Parameter &rArr; '<code>root</code>'</a></li>
-    <li><a href="#compat-only_with_tag>'<code>only_with_tag</code>' Parameter &rArr; '<code>pathrev</code>'</a></li>
+    <li><a href="#compat-only_with_tag">'<code>only_with_tag</code>' Parameter &rArr; '<code>pathrev</code>'</a></li>
     <li><a href="#compat-oldcheckout">'<code>~checkout~</code>' Magic Path Prefix &rArr; '<code>*checkout*</code>'</a></li>
     <li><a href="#compat-checkout">'<code>*checkout*</code>' Magic Path Prefix &rArr; '<code>view=co</code>'</a></li>
     <li><a href="#compat-root">'<code>root</code>' Parameter &rArr; Root Path Component</a></li>
@@ -98,7 +98,7 @@
     <li><a href="#compat-tarball">'<code>tarball=1</code>' Parameter &rArr; '<code>view=tar</code>'</a></li>
     <li><a href="#compat-graph">'<code>graph=1</code>' Parameter &rArr; '<code>view=graph</code>'</a></li>
     <li><a href="#compat-makeimage">'<code>graph=1&makeimage=1</code>' Parameters &rArr; '<code>view=graphimg</code>'</a></li>
-    <li><a href="#compat-content_type">'<code>content_type=text/vnd.viewcvs-markup</code>' and '<code>content_type=text/x-cvsweb-markup</code>' Parameters&rArr; '<code>view=markup</code>'
+    <li><a href="#compat-content_type">'<code>content-type=text/vnd.viewcvs-markup</code>' and '<code>content-type=text/x-cvsweb-markup</code>' Parameters&rArr; '<code>view=markup</code>'
     <li><a href="#compat-attic">'<code>Attic/FILE</code>' Paths &rArr; '<code>FILE</code>'</a></li>
   </ul>
 </div>
@@ -1013,6 +1013,17 @@
         of author match</td>
   </tr>
   <tr>
+    <td><code>comment=<var>COMMENT</var></code></td>
+    <td>optional</td>
+    <td>log message query string</td>
+  </tr>
+  <tr>
+    <td><code>comment_match=<var>COMMENT_MATCH</var></code></td>
+    <td>optional</td>
+    <td>"exact" "like" "glob" "regex" or "notregex" determining type
+        of log message match</td>
+  </tr>
+  <tr>
     <td><code>querysort=SORT</code></td>
     <td>optional</td>
     <td>"date" "author" or "file" determining order of query results</td>
@@ -1456,11 +1467,14 @@
 
 <h3 id="compat-content_type">'<code>content_type=text/vnd.viewcvs-markup</code>' and '<code>content_type=text/x-cvsweb-markup</code>' Parameters&rArr; '<code>view=markup</code>'</h3>
 
-<p><code>content_type=text/vnd.viewcvs-markup</code> and
-   <code>content_type=text/x-cvsweb-markup</code> parameters are
+<p><code>content-type=text/vnd.viewcvs-markup</code> and
+   <code>content-type=text/x-cvsweb-markup</code> parameters are
    treated like a <code>view=markup</code> parameter. There is
    currently no redirect when it is encountered, but there could be
-   one in the future.</p>
+   one in the future.  Other values of the <code>content-type</code>
+   parameter, which were used to dictate the MIME type of files
+   displayed in the checkout/download view prior to ViewVC 1.0.6, are
+   ignored.</p>
 
 <h3 id="compat-attic">'<code>Attic/FILE</code>' Paths &rArr; '<code>FILE</code>'</h3>
 

Modified: trunk/lib/blame.py
==============================================================================
--- trunk/lib/blame.py	(original)
+++ trunk/lib/blame.py	Mon Nov  3 09:57:02 2008
@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 # -*-python-*-
 #
-# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
+# Copyright (C) 1999-2007 The ViewCVS Group. All Rights Reserved.
 # Copyright (C) 2000 Curt Hagenlocher <curt hagenlocher org>
 #
 # By using this file, you agree to the terms and conditions set forth in
@@ -34,7 +34,6 @@
 import math
 import cgi
 import vclib
-import vclib.ccvs.blame
 
 
 re_includes = re.compile('\\#(\\s*)include(\\s*)"(.*?)"')
@@ -101,6 +100,7 @@
 
 
 def make_html(root, rcs_path):
+  import vclib.ccvs.blame
   bs = vclib.ccvs.blame.BlameSource(os.path.join(root, rcs_path))
 
   line = 0

Modified: trunk/lib/config.py
==============================================================================
--- trunk/lib/config.py	(original)
+++ trunk/lib/config.py	Mon Nov  3 09:57:02 2008
@@ -1,6 +1,6 @@
 # -*-python-*-
 #
-# Copyright (C) 1999-2007 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
 # the LICENSE.html file which can be found at the top level of the ViewVC
@@ -39,8 +39,7 @@
 
 class Config:
   _sections = ('general', 'utilities', 'options', 'cvsdb', 'templates')
-  _force_multi_value = ('cvs_roots', 'forbidden',
-                        'svn_roots', 'languages', 'kv_files',
+  _force_multi_value = ('cvs_roots', 'svn_roots', 'languages', 'kv_files',
                         'root_parents', 'allowed_views')
 
   def __init__(self):
@@ -163,18 +162,32 @@
       return
     self._process_root_options(self.parser, rootname)
 
+  def _get_parser_items(self, parser, section):
+    """Basically implement ConfigParser.items() for pre-Python-2.3 versions."""
+    try:
+      return self.parser.items(section)
+    except AttributeError:
+      d = {}
+      for option in parser.options(section):
+        d[option] = parser.get(section, option)
+      return d.items()
+    
   def get_authorizer_params(self, authorizer, rootname=None):
     if not self.conf_path:
       return {}
-    
+
     params = {}
     authz_section = 'authz-%s' % (authorizer)
+    for section in self.parser.sections():
+      if section == authz_section:
+        for key, value in self._get_parser_items(self.parser, section):
+          params[key] = value
     if rootname:
       root_authz_section = 'root-%s/authz-%s' % (rootname, authorizer)
-    for section in self.parser.sections():
-      if section == authz_section \
-         or (rootname and section == root_authz_section):
-        params.update(self.parser.items(section))
+      for section in self.parser.sections():
+        if section == root_authz_section:
+          for key, value in self._get_parser_items(self.parser, section):
+            params[key] = value
     return params
   
   def set_defaults(self):
@@ -185,7 +198,7 @@
     self.general.root_parents = []
     self.general.default_root = ''
     self.general.mime_types_file = ''
-    self.general.address = '<a href="mailto:user insert your domain here">No admin address has been configured</a>'
+    self.general.address = ''
     self.general.kv_files = [ ]
     self.general.languages = ['en-us']
 
@@ -196,36 +209,31 @@
       self.utilities.cvsnt = None
     self.utilities.svn = ''
     self.utilities.diff = ''
-    self.utilities.enscript = ''
-    self.utilities.highlight = ''
-    self.utilities.source_highlight = ''
-    self.utilities.py2html_dir = '.'
-    self.utilities.php = 'php'
     self.utilities.cvsgraph = ''
-    self.utilities.gzip = ''
-    self.utilities.sed = ''
 
     self.options.root_as_url_component = 1
     self.options.checkout_magic = 0
-    self.options.allowed_views = ['markup', 'annotate']
+    self.options.allowed_views = ['markup', 'annotate', 'roots']
     self.options.authorizer = 'forbidden'
     self.options.mangle_email_addresses = 0
     self.options.default_file_view = "log"
     self.options.http_expiration_time = 600
     self.options.generate_etags = 1
+    self.options.svn_config_dir = None
     self.options.use_rcsparse = 0
     self.options.sort_by = 'file'
     self.options.sort_group_dirs = 1
     self.options.hide_attic = 1
+    self.options.hide_errorful_entries = 0
     self.options.log_sort = 'date'
     self.options.diff_format = 'h'
     self.options.hide_cvsroot = 1
     self.options.hr_breakable = 1
     self.options.hr_funout = 1
-    self.options.hr_ignore_white = 1
+    self.options.hr_ignore_white = 0
     self.options.hr_ignore_keyword_subst = 1
     self.options.hr_intraline = 0
-    self.options.allow_compress = 1
+    self.options.allow_compress = 0
     self.options.template_dir = "templates"
     self.options.docroot = None
     self.options.show_subdir_lastmod = 0
@@ -234,27 +242,19 @@
     self.options.cross_copies = 0
     self.options.use_localtime = 0
     self.options.short_log_len = 80
-    self.options.use_py2html = 0
-    self.options.use_enscript = 0
-    self.options.use_highlight = 0
-    self.options.highlight_line_numbers = 1
-    self.options.highlight_convert_tabs = 2
-    self.options.use_source_highlight = 0
-    self.options.source_highlight_line_numbers = 1
-    self.options.use_php = 0
+    self.options.enable_syntax_coloration = 1
     self.options.use_cvsgraph = 0
     self.options.cvsgraph_conf = "cvsgraph.conf"
     self.options.use_re_search = 0
     self.options.use_pagesize = 0
     self.options.limit_changes = 100
 
-    self.templates.annotate = None
     self.templates.diff = None
     self.templates.directory = None
     self.templates.error = None
+    self.templates.file = None
     self.templates.graph = None
     self.templates.log = None
-    self.templates.markup = None
     self.templates.query = None
     self.templates.query_form = None
     self.templates.query_results = None
@@ -270,6 +270,7 @@
     self.cvsdb.readonly_passwd = '' 
     self.cvsdb.row_limit = 1000
     self.cvsdb.rss_row_limit = 100
+    self.cvsdb.check_database_for_root = 0
 
 def _startswith(somestr, substr):
   return somestr[:len(substr)] == substr

Modified: trunk/lib/cvsdb.py
==============================================================================
--- trunk/lib/cvsdb.py	(original)
+++ trunk/lib/cvsdb.py	Mon Nov  3 09:57:02 2008
@@ -17,6 +17,7 @@
 import fnmatch
 import re
 
+import vclib
 import dbi
 
 
@@ -457,9 +458,13 @@
 
         return commit
 
-    def sql_delete(self, table, key, value):
+    def sql_delete(self, table, key, value, keep_fkey = None):
         sql = "DELETE FROM %s WHERE %s=%%s" % (table, key)
         sql_args = (value, )
+        if keep_fkey:
+          sql += " AND %s NOT IN (SELECT %s FROM checkins WHERE %s = %%s)" \
+                 % (key, keep_fkey, keep_fkey)
+          sql_args = (value, value)
         cursor = self.db.cursor()
         cursor.execute(sql, sql_args)
         
@@ -480,16 +485,16 @@
                  plus_count, minus_count, description_id) = cursor.fetchone()
             except TypeError:
                 break
-            checkins.append([file_id, dir_id, branch_id, description_id])
+            checkins.append([file_id, dir_id, branch_id, description_id, who_id])
 
         #self.sql_delete('repositories', 'id', rep_id)
         self.sql_delete('checkins', 'repositoryid', rep_id)
         for checkin in checkins:
-            self.sql_delete('files', 'id', checkin[0])
-            self.sql_delete('dirs', 'id', checkin[1])
-            self.sql_delete('branches', 'id', checkin[2])
-            self.sql_delete('descs', 'id', checkin[3])
-
+            self.sql_delete('files', 'id', checkin[0], 'fileid')
+            self.sql_delete('dirs', 'id', checkin[1], 'dirid')
+            self.sql_delete('branches', 'id', checkin[2], 'branchid')
+            self.sql_delete('descs', 'id', checkin[3], 'descid')
+            self.sql_delete('people', 'id', checkin[4], 'whoid')
 
 ## the Commit class holds data on one commit, the representation is as
 ## close as possible to how it should be committed and retrieved to the
@@ -771,7 +776,8 @@
     directory = string.join(path_parts[:-1], "/")
     file = path_parts[-1]
 
-    revs = repository.itemlog(path_parts, revision, {"cvs_pass_rev": 1})
+    revs = repository.itemlog(path_parts, revision, vclib.SORTBY_DEFAULT,
+                              0, 0, {"cvs_pass_rev": 1})
     for rev in revs:
         commit = CreateCommit()
         commit.SetRepository(repository.rootpath)

Modified: trunk/lib/debug.py
==============================================================================
--- trunk/lib/debug.py	(original)
+++ trunk/lib/debug.py	Mon Nov  3 09:57:02 2008
@@ -1,6 +1,6 @@
 # -*-python-*-
 #
-# Copyright (C) 1999-2006 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
 # the LICENSE.html file which can be found at the top level of the ViewVC
@@ -68,12 +68,6 @@
     return "ViewVC Unrecoverable Error: %s" % self.msg
 
 
-class ViewVCNotAuthorizedException(ViewVCException):
-  def __init__(self, user, what):
-    msg = 'User "%s" is not authorized to see %s' % (user, what)
-    ViewVCException.__init__(self, msg, '501 Not Authorized')
-
-
 def PrintException(server, exc_data):
   status = exc_data['status']
   msg = exc_data['msg']

Modified: trunk/lib/popen.py
==============================================================================
--- trunk/lib/popen.py	(original)
+++ trunk/lib/popen.py	Mon Nov  3 09:57:02 2008
@@ -1,6 +1,6 @@
 # -*-python-*-
 #
-# Copyright (C) 1999-2006 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
 # the LICENSE.html file which can be found at the top level of the ViewVC

Modified: trunk/lib/query.py
==============================================================================
--- trunk/lib/query.py	(original)
+++ trunk/lib/query.py	Mon Nov  3 09:57:02 2008
@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 # -*-python-*-
 #
-# Copyright (C) 1999-2007 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
 # the LICENSE.html file which can be found at the top level of the ViewVC
@@ -390,6 +390,13 @@
     commits.append(build_commit(server, cfg, current_desc, files,
                                 cvsroots, viewvc_link))
 
+    # Strip out commits that don't have any files attached to them.  The
+    # files probably aren't present because they've been blocked via
+    # forbiddenness.
+    def _only_with_files(commit):
+        return len(commit.files) > 0
+    commits = filter(_only_with_files, commits)
+  
     return commits
 
 def main(server, cfg, viewvc_link):

Modified: trunk/lib/vcauth/__init__.py
==============================================================================
--- trunk/lib/vcauth/__init__.py	(original)
+++ trunk/lib/vcauth/__init__.py	Mon Nov  3 09:57:02 2008
@@ -1,6 +1,6 @@
 # -*-python-*-
 #
-# Copyright (C) 2006 The ViewCVS Group. All Rights Reserved.
+# Copyright (C) 2006-2008 The ViewCVS Group. All Rights Reserved.
 #
 # By using this file, you agree to the terms and conditions set forth in
 # the LICENSE.html file which can be found at the top level of the ViewVC
@@ -19,39 +19,31 @@
 class GenericViewVCAuthorizer:
   """Abstract class encapsulating version control authorization routines."""
   
-  def __init__(self, username, root, params={}):
+  def __init__(self, username=None, params={}):
     """Create a GenericViewVCAuthorizer object which will be used to
     validate that USERNAME has the permissions needed to view version
-    control repository ROOT (in whole or in part).  PARAMS is a
-    dictionary of custom parameters for the authorizer.
-
-    Raise ViewVCRootAccessNotAuthorized error if USERNAME isn't
-    allowed to see this repository at all."""
+    control repositories (in whole or in part).  PARAMS is a
+    dictionary of custom parameters for the authorizer."""
     pass
 
-  def check_path_access(self, path_parts, rev=None):
+  def check_root_access(self, rootname):
+    """Return 1 iff the associated username is permitted to read ROOTNAME."""
+    pass
+  
+  def check_path_access(self, rootname, path_parts, pathtype, rev=None):
     """Return 1 iff the associated username is permitted to read
-    revision REV of the path PATH_PARTS in the repository associated
-    with this authorizer."""
+    revision REV of the path PATH_PARTS (of type PATHTYPE) in
+    repository ROOTNAME."""
     pass
 
 
-class ViewVCRootAccessNotAuthorized(Exception):
-  def __init__(self, rootname, username):
-    self.rootname = rootname
-    self.username = username
-  def __str__(self):
-    return "Access to root '%s' by user '%s' is denied." \
-           % (self.rootname, self.username)
-
-
 
 ##############################################################################
 
 class ViewVCAuthorizer(GenericViewVCAuthorizer):
   """The uber-permissive authorizer."""
-  def __init__(self):
-    pass
+  def check_root_access(self, rootname):
+    return 1
     
-  def check_path_access(self, path_parts, rev=None):
+  def check_path_access(self, rootname, path_parts, pathtype, rev=None):
     return 1

Modified: trunk/lib/vcauth/forbidden/__init__.py
==============================================================================
--- trunk/lib/vcauth/forbidden/__init__.py	(original)
+++ trunk/lib/vcauth/forbidden/__init__.py	Mon Nov  3 09:57:02 2008
@@ -1,6 +1,6 @@
 # -*-python-*-
 #
-# Copyright (C) 2006 The ViewCVS Group. All Rights Reserved.
+# Copyright (C) 2006-2008 The ViewCVS Group. All Rights Reserved.
 #
 # By using this file, you agree to the terms and conditions set forth in
 # the LICENSE.html file which can be found at the top level of the ViewVC
@@ -16,25 +16,24 @@
 
 class ViewVCAuthorizer(vcauth.GenericViewVCAuthorizer):
   """A simple top-level module authorizer."""
-  def __init__(self, username, root, params={}):
+  def __init__(self, username, params={}):
     forbidden = params.get('forbidden', '')
-    self.root = root
     self.forbidden = map(string.strip,
                          filter(None, string.split(forbidden, ',')))
-    
-  def check_path_access(self, path_parts, rev=None):
+
+  def check_root_access(self, rootname):
+    return 1
+  
+  def check_path_access(self, rootname, path_parts, pathtype, rev=None):
     # No path?  No problem.
     if not path_parts:
       return 1
 
-    # If we have a single path part, we can't tell if this is a file
-    # or a directory.  So we ask our version control system.  If it's
-    # not a directory, we don't care about it.
-    if len(path_parts) == 1:
-      if self.root.itemtype(path_parts, rev) != vclib.DIR:
-        return 1
+    # Not a directory?  We aren't interested.
+    if pathtype != vclib.DIR:
+      return 1
 
-    # At this point we're looking a path we believe to be a directory.
+    # At this point we're looking at a directory path.
     module = path_parts[0]
     default = 1
     for pat in self.forbidden:

Modified: trunk/lib/vcauth/svnauthz/__init__.py
==============================================================================
--- trunk/lib/vcauth/svnauthz/__init__.py	(original)
+++ trunk/lib/vcauth/svnauthz/__init__.py	Mon Nov  3 09:57:02 2008
@@ -1,6 +1,6 @@
 # -*-python-*-
 #
-# Copyright (C) 2006 The ViewCVS Group. All Rights Reserved.
+# Copyright (C) 2006-2008 The ViewCVS Group. All Rights Reserved.
 #
 # By using this file, you agree to the terms and conditions set forth in
 # the LICENSE.html file which can be found at the top level of the ViewVC
@@ -21,20 +21,34 @@
 class ViewVCAuthorizer(vcauth.GenericViewVCAuthorizer):
   """Subversion authz authorizer module"""
   
-  def __init__(self, username, root, params={}):
-    rootname = root.rootname()
-    self.paths = {}   # paths-in-root -> access boolean for USERNAME
+  def __init__(self, username, params={}):
+    self.username = username
+    self.rootpaths = { }  # {root -> { paths -> access boolean for USERNAME }}
     
     # Get the authz file location from a passed-in parameter.
-    authz_file = params.get('authzfile')
-    if not authz_file:
+    self.authz_file = params.get('authzfile')
+    if not self.authz_file:
       raise debug.ViewVCException("No authzfile configured")
-    if not os.path.exists(authz_file):
+    if not os.path.exists(self.authz_file):
       raise debug.ViewVCException("Configured authzfile file not found")
 
+  def _get_paths_for_root(self, rootname):
+    if self.rootpaths.has_key(rootname):
+      return self.rootpaths[rootname]
+
+    paths_for_root = { }
+    
     # Parse the authz file.
     cp = ConfigParser()
-    cp.read(authz_file)
+    cp.read(self.authz_file)
+
+    # Figure out if there are any aliases for the current username
+    aliases = []
+    if cp.has_section('aliases'):
+      for alias in cp.options('aliases'):
+        entry = cp.get('aliases', alias)
+        if entry == self.username:
+          aliases.append(alias)
 
     # Figure out which groups USERNAME has a part of.
     groups = []
@@ -69,12 +83,15 @@
         entries = string.split(cp.get('groups', groupname), ',')
         for entry in entries:
           entry = string.strip(entry)
-          if entry == username:
+          if entry == self.username:
             group_member = 1
             break
           elif entry[0:1] == "@" and _process_group(entry[1:]):
             group_member = 1
             break
+          elif entry[0:1] == "&" and entry[1:] in aliases:
+            group_member = 1
+            break
         if group_member:
           groups.append(groupname)
         return group_member
@@ -83,31 +100,31 @@
       for group in cp.options('groups'):
         _process_group(group)
 
-    # Read the other (non-"groups") sections, and figure out in which
-    # repositories USERNAME or his groups have read rights.
-    root_is_readable = 0
-    for section in cp.sections():
-
-      # Skip the "groups" section -- we handled that already.
-      if section == 'groups':
-        continue
-
-      # Skip sections not related to our rootname.  While we're at it,
-      # go ahead and figure out the repository path we're talking about.
-      if section.find(':') == -1:
-        path = section
-      else:
-        root, path = string.split(section, ':', 1)
-        if root != rootname:
-          continue
-
+    def _userspec_matches_user(userspec):
+      # If there is an inversion character, recurse and return the
+      # opposite result.
+      if userspec[0:1] == '~':
+        return not _userspec_matches_user(userspec[1:])
+
+      # See if the userspec applies to our current user.
+      return userspec == '*' \
+             or userspec == self.username \
+             or (self.username is not None and userspec == "$authenticated") \
+             or (self.username is None and userspec == "$anonymous") \
+             or (userspec[0:1] == "@" and userspec[1:] in groups) \
+             or (userspec[0:1] == "&" and userspec[1:] in aliases)
+      
+    def _process_access_section(section):
+      """Inline function for determining user access in a single
+      config secction.  Return a two-tuple (ALLOW, DENY) containing
+      the access determination for USERNAME in a given authz file
+      SECTION (if any)."""
+  
       # Figure if this path is explicitly allowed or denied to USERNAME.
       allow = deny = 0
       for user in cp.options(section):
         user = string.strip(user)
-        if user == '*' \
-           or user == username \
-           or (user[0:1] == "@" and user[1:] in groups):
+        if _userspec_matches_user(user):
           # See if the 'r' permission is among the ones granted to
           # USER.  If so, we can stop looking.  (Entry order is not
           # relevant -- we'll use the most permissive entry, meaning
@@ -116,28 +133,91 @@
           deny = not allow
           if allow:
             break
+      return allow, deny
+    
+    # Read the other (non-"groups") sections, and figure out in which
+    # repositories USERNAME or his groups have read rights.  We'll
+    # first check groups that have no specific repository designation,
+    # then superimpose those that have a repository designation which
+    # matches the one we're asking about.
+    root_sections = []
+    for section in cp.sections():
+
+      # Skip the "groups" section -- we handled that already.
+      if section == 'groups':
+        continue
+      
+      if section == 'aliases':
+        continue
+
+      # Process root-agnostic access sections; skip (but remember)
+      # root-specific ones that match our root; ignore altogether
+      # root-specific ones that don't match our root.  While we're at
+      # it, go ahead and figure out the repository path we're talking
+      # about.
+      if section.find(':') == -1:
+        path = section
+      else:
+        name, path = string.split(section, ':', 1)
+        if name == rootname:
+          root_sections.append(section)
+        continue
+
+      # Check for a specific access determination.
+      allow, deny = _process_access_section(section)
           
       # If we got an explicit access determination for this path and this
       # USERNAME, record it.
       if allow or deny:
-        if allow:
-          root_is_readable = 1
         if path != '/':
           path = '/' + string.join(filter(None, string.split(path, '/')), '/')
-        self.paths[path] = allow
+        paths_for_root[path] = allow
 
-    # If USERNAME can't see this root at all, raise an error.
+    # Okay.  Superimpose those root-specific values now.
+    for section in root_sections:
+
+      # Get the path again.
+      name, path = string.split(section, ':', 1)
+      
+      # Check for a specific access determination.
+      allow, deny = _process_access_section(section)
+                
+      # If we got an explicit access determination for this path and this
+      # USERNAME, record it.
+      if allow or deny:
+        if path != '/':
+          path = '/' + string.join(filter(None, string.split(path, '/')), '/')
+        paths_for_root[path] = allow
+
+    # If the root isn't readable, there's no point in caring about all
+    # the specific paths the user can't see.  Just point the rootname
+    # to a None paths dictionary.
+    root_is_readable = 0
+    for path in paths_for_root.keys():
+      if paths_for_root[path]:
+        root_is_readable = 1
+        break
     if not root_is_readable:
-      raise vcauth.ViewVCRootAccessNotAuthorized(rootname, username)
+      paths_for_root = None
+      
+    self.rootpaths[rootname] = paths_for_root
+    return paths_for_root
 
-  def check_path_access(self, path_parts, rev=None):
+  def check_root_access(self, rootname):
+    paths = self._get_paths_for_root(rootname)
+    return (paths is not None) and 1 or 0
+  
+  def check_path_access(self, rootname, path_parts, pathtype, rev=None):
     # Crawl upward from the path represented by PATH_PARTS toward to
     # the root of the repository, looking for an explicitly grant or
     # denial of access.
+    paths = self._get_paths_for_root(rootname)
+    if paths is None:
+      return 0
     parts = path_parts[:]
     while parts:
       path = '/' + string.join(parts, '/')
-      if self.paths.has_key(path):
-        return self.paths[path]
+      if paths.has_key(path):
+        return paths[path]
       del parts[-1]
-    return self.paths.get('/', 0)
+    return paths.get('/', 0)

Modified: trunk/lib/vclib/__init__.py
==============================================================================
--- trunk/lib/vclib/__init__.py	(original)
+++ trunk/lib/vclib/__init__.py	Mon Nov  3 09:57:02 2008
@@ -1,6 +1,6 @@
 # -*-python-*-
 #
-# Copyright (C) 1999-2007 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
 # the LICENSE.html file which can be found at the top level of the ViewVC
@@ -37,6 +37,12 @@
 REPLACED   = 'replaced'
 MODIFIED   = 'modified'
 
+# log sort keys
+SORTBY_DEFAULT = 0  # default/no sorting
+SORTBY_DATE    = 1  # sorted by date, youngest first
+SORTBY_REV     = 2  # sorted by revision, youngest first
+
+
 # ======================================================================
 #
 class Repository:
@@ -50,6 +56,13 @@
 
   def rootpath(self):
     """Return the location of this repository."""
+
+  def authorizer(self):
+    """Return the vcauth.Authorizer object associated with this
+    repository, or None if no such association has been made."""
+    
+  def open(self):
+    """Open a connection to the repository."""
     
   def itemtype(self, path_parts, rev):
     """Return the type of the item (file or dir) at the given path and revision
@@ -94,7 +107,7 @@
     New properties will be set on all of the DirEntry objects in the entries
     list. At the very least, a "rev" property will be set to a revision
     number or None if the entry doesn't have a number. Other properties that
-    may be set include "date", "author", and "log".
+    may be set include "date", "author", "log", "size", and "lockinfo".
 
     The path is specified as a list of components, relative to the root
     of the repository. e.g. ["subdir1", "subdir2", "filename"]
@@ -108,7 +121,7 @@
     options is a dictionary of implementation specific options
     """
   
-  def itemlog(self, path_parts, rev, options):
+  def itemlog(self, path_parts, rev, sortby, first, limit, options):
     """Retrieve an item's log information
 
     The result is a list of Revision objects
@@ -118,9 +131,28 @@
 
     rev is the revision of the item to return information about
 
+    sortby indicates the way in which the returned list should be
+    sorted (SORTBY_DEFAULT, SORTBY_DATE, SORTBY_REV)
+
+    first is the 0-based index of the first Revision returned (after
+    sorting, if any, has occured)
+
+    limit is the maximum number of returned Revisions, or 0 to return
+    all available data
+    
     options is a dictionary of implementation specific options
     """
 
+  def itemprops(self, path_parts, rev):
+    """Return a dictionary mapping property names to property values
+    for properties stored on an item.
+
+    The path is specified as a list of components, relative to the root
+    of the repository. e.g. ["subdir1", "subdir2", "filename"]
+
+    rev is the revision of the item to return information about.
+    """
+    
   def rawdiff(self, path_parts1, rev1, path_parts2, rev2, type, options={}):
     """Return a diff (in GNU diff format) of two file revisions
 
@@ -155,6 +187,17 @@
     doesn't support a global revision concept.
     """
 
+  def isexecutable(self, path_parts, rev):
+    """Return true iff a given revision of a versioned file is to be
+    considered an executable program or script.
+
+    The path is specified as a list of components, relative to the root
+    of the repository. e.g. ["subdir1", "subdir2", "filename"]
+
+    rev is the revision of the item to return information about
+    """
+    
+    
 # ======================================================================
 class DirEntry:
   """Instances represent items in a directory listing"""
@@ -173,7 +216,7 @@
 class Revision:
   """Instances holds information about revisions of versioned resources"""
 
-  def __init__(self, number, string, date, author, changed, log, size):
+  def __init__(self, number, string, date, author, changed, log, size, lockinfo):
     """Create a new Revision() item:
           NUMBER:  Revision in an integer-based, sortable format
           STRING:  Revision as a string
@@ -182,6 +225,7 @@
           CHANGED:  Lines-changed (contextual diff) information
           LOG:  Log message associated with the creation of this revision
           SIZE:  Size (in bytes) of this revision's fulltext (files only)
+          LOCKINFO:  Information about locks held on this revision
     """
     self.number = number
     self.string = string
@@ -190,6 +234,7 @@
     self.changed = changed
     self.log = log
     self.size = size
+    self.lockinfo = lockinfo
 
   def __cmp__(self, other):
     return cmp(self.number, other.number)
@@ -350,3 +395,26 @@
   def _label(self, (path, date, rev)):
     date = date and time.strftime('%Y/%m/%d %H:%M:%S', time.gmtime(date))
     return "%s\t%s\t%s" % (path, date, rev)
+
+
+def check_root_access(repos):
+  """Return 1 iff the associated username is permitted to read REPOS,
+  as determined by consulting REPOS's Authorizer object (if any)."""
+
+  auth = repos.authorizer()
+  if not auth:
+    return 1
+  return auth.check_root_access(repos.rootname())
+  
+def check_path_access(repos, path_parts, pathtype=None, rev=None):
+  """Return 1 iff the associated username is permitted to read
+  revision REV of the path PATH_PARTS (of type PATHTYPE) in repository
+  REPOS, as determined by consulting REPOS's Authorizer object (if any)."""
+
+  auth = repos.authorizer()
+  if not auth:
+    return 1
+  if not pathtype:
+    pathtype = repos.itemtype(path_parts, rev)
+  return auth.check_path_access(repos.rootname(), path_parts, pathtype, rev)
+

Modified: trunk/lib/vclib/ccvs/__init__.py
==============================================================================
--- trunk/lib/vclib/ccvs/__init__.py	(original)
+++ trunk/lib/vclib/ccvs/__init__.py	Mon Nov  3 09:57:02 2008
@@ -1,6 +1,6 @@
 # -*-python-*-
 #
-# Copyright (C) 1999-2006 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
 # the LICENSE.html file which can be found at the top level of the ViewVC
@@ -9,347 +9,35 @@
 # For more information, visit http://viewvc.org/
 #
 # -----------------------------------------------------------------------
-
-"""
-This is a Version Control library driver for locally accessible cvs-repositories.
-"""
-
 import os
-import string
-import re
-import cStringIO
-import tempfile
-
-import vclib
-import rcsparse
-import blame
-
-### The functionality shared with bincvs should probably be moved to a
-### separate module
-from vclib.bincvs import CVSRepository, Revision, Tag, \
-                         _file_log, _log_path
-
-class CCVSRepository(CVSRepository):
-  def dirlogs(self, path_parts, rev, entries, options):
-    """see vclib.Repository.dirlogs docstring
-
-    rev can be a tag name or None. if set only information from revisions
-    matching the tag will be retrieved
-
-    Option values recognized by this implementation:
-
-      cvs_subdirs
-        boolean. true to fetch logs of the most recently modified file in each
-        subdirectory
-
-    Option values returned by this implementation:
-
-      cvs_tags, cvs_branches
-        lists of tag and branch names encountered in the directory
-    """
-    subdirs = options.get('cvs_subdirs', 0)
-
-    dirpath = self._getpath(path_parts)
-    alltags = {           # all the tags seen in the files of this dir
-      'MAIN' : '',
-      'HEAD' : '1.1'
-    }
-
-    for entry in entries:
-      entry.rev = entry.date = entry.author = entry.dead = entry.log = None
-      path = _log_path(entry, dirpath, subdirs)
-      if path:
-        entry.path = path
-        try:
-          rcsparse.parse(open(path, 'rb'), InfoSink(entry, rev, alltags))
-        except IOError, e:
-          entry.errors.append("rcsparse error: %s" % e)
-        except RuntimeError, e:
-          entry.errors.append("rcsparse error: %s" % e)
-        except rcsparse.RCSStopParser:
-          pass
-
-    branches = options['cvs_branches'] = []
-    tags = options['cvs_tags'] = []
-    for name, rev in alltags.items():
-      if Tag(None, rev).is_branch:
-        branches.append(name)
-      else:
-        tags.append(name)
-
-  def itemlog(self, path_parts, rev, options):
-    """see vclib.Repository.itemlog docstring
-
-    rev parameter can be a revision number, a branch number, a tag name,
-    or None. If None, will return information about all revisions, otherwise,
-    will only return information about the specified revision or branch.
-
-    Option values returned by this implementation:
-
-      cvs_tags
-        dictionary of Tag objects for all tags encountered
-    """
-    path = self.rcsfile(path_parts, 1)
-    sink = TreeSink()
-    rcsparse.parse(open(path, 'rb'), sink)
-    filtered_revs = _file_log(sink.revs.values(), sink.tags,
-                              sink.default_branch, rev)
-    for rev in filtered_revs:
-      if rev.prev and len(rev.number) == 2:
-        rev.changed = rev.prev.next_changed
-    options['cvs_tags'] = sink.tags
-
-    return filtered_revs
-
-  def rawdiff(self, path_parts1, rev1, path_parts2, rev2, type, options={}):
-    temp1 = tempfile.mktemp()
-    open(temp1, 'wb').write(self.openfile(path_parts1, rev1)[0].getvalue())
-    temp2 = tempfile.mktemp()
-    open(temp2, 'wb').write(self.openfile(path_parts2, rev2)[0].getvalue())
-
-    r1 = self.itemlog(path_parts1, rev1, {})[-1]
-    r2 = self.itemlog(path_parts2, rev2, {})[-1]
-
-    info1 = (self.rcsfile(path_parts1, root=1, v=0), r1.date, r1.string)
-    info2 = (self.rcsfile(path_parts2, root=1, v=0), r2.date, r2.string)
-
-    diff_args = vclib._diff_args(type, options)
-
-    return vclib._diff_fp(temp1, temp2, info1, info2,
-                          self.utilities.diff or 'diff', diff_args)
-
-  def annotate(self, path_parts, rev=None):
-    source = blame.BlameSource(self.rcsfile(path_parts, 1), rev)
-    return source, source.revision
-
-  def revinfo(self, rev):
-    raise vclib.UnsupportedFeature
-
-  def openfile(self, path_parts, rev=None):
-    path = self.rcsfile(path_parts, 1)
-    sink = COSink(rev)
-    rcsparse.parse(open(path, 'rb'), sink)
-    revision = sink.last and sink.last.string
-    return cStringIO.StringIO(string.join(sink.sstext.text, "\n")), revision
-
-class MatchingSink(rcsparse.Sink):
-  """Superclass for sinks that search for revisions based on tag or number"""
-
-  def __init__(self, find):
-    """Initialize with tag name or revision number string to match against"""
-    if not find or find == 'MAIN' or find == 'HEAD':
-      self.find = None
-    else:
-      self.find = find
-
-    self.find_tag = None
-
-  def set_principal_branch(self, branch_number):
-    if self.find is None:
-      self.find_tag = Tag(None, branch_number)
-
-  def define_tag(self, name, revision):
-    if name == self.find:
-      self.find_tag = Tag(None, revision)
-
-  def admin_completed(self):
-    if self.find_tag is None:
-      if self.find is None:
-        self.find_tag = Tag(None, '')
-      else:
-        try:
-          self.find_tag = Tag(None, self.find)
-        except ValueError:
-          pass
-
-class InfoSink(MatchingSink):
-  def __init__(self, entry, tag, alltags):
-    MatchingSink.__init__(self, tag)
-    self.entry = entry
-    self.alltags = alltags
-    self.matching_rev = None
-    self.perfect_match = 0
-
-  def define_tag(self, name, revision):
-    MatchingSink.define_tag(self, name, revision)
-    self.alltags[name] = revision
-
-  def admin_completed(self):
-    MatchingSink.admin_completed(self)
-    if self.find_tag is None:
-      # tag we're looking for doesn't exist
-      raise rcsparse.RCSStopParser
-
-  def define_revision(self, revision, date, author, state, branches, next):
-    if self.perfect_match:
-      return
-
-    tag = self.find_tag
-    rev = Revision(revision, date, author, state == "dead")
-
-    # perfect match if revision number matches tag number or if revision is on
-    # trunk and tag points to trunk. imperfect match if tag refers to a branch
-    # and this revision is the highest revision so far found on that branch
-    perfect = ((rev.number == tag.number) or
-               (not tag.number and len(rev.number) == 2))
-    if perfect or (tag.is_branch and tag.number == rev.number[:-1] and
-                   (not self.matching_rev or
-                    rev.number > self.matching_rev.number)):
-      self.matching_rev = rev
-      self.perfect_match = perfect
-
-  def set_revision_info(self, revision, log, text):
-    if self.matching_rev:
-      if revision == self.matching_rev.string:
-        self.entry.rev = self.matching_rev.string
-        self.entry.date = self.matching_rev.date
-        self.entry.author = self.matching_rev.author
-        self.entry.dead = self.matching_rev.dead
-        self.entry.log = log
-        raise rcsparse.RCSStopParser
-    else:
-      raise rcsparse.RCSStopParser
-
-class TreeSink(rcsparse.Sink):
-  d_command = re.compile('^d(\d+)\\s(\\d+)')
-  a_command = re.compile('^a(\d+)\\s(\\d+)')
-
-  def __init__(self):
-    self.revs = { }
-    self.tags = { }
-    self.head = None
-    self.default_branch = None
-
-  def set_head_revision(self, revision):
-    self.head = revision
-
-  def set_principal_branch(self, branch_number):
-    self.default_branch = branch_number
-
-  def define_tag(self, name, revision):
-    # check !tags.has_key(tag_name)
-    self.tags[name] = revision
-
-  def define_revision(self, revision, date, author, state, branches, next):
-    # check !revs.has_key(revision)
-    self.revs[revision] = Revision(revision, date, author, state == "dead")
-
-  def set_revision_info(self, revision, log, text):
-    # check revs.has_key(revision)
-    rev = self.revs[revision]
-    rev.log = log
-
-    changed = None
-    added = 0
-    deled = 0
-    if self.head != revision:
-      changed = 1
-      lines = string.split(text, '\n')
-      idx = 0
-      while idx < len(lines):
-        command = lines[idx]
-        dmatch = self.d_command.match(command)
-        idx = idx + 1
-        if dmatch:
-          deled = deled + string.atoi(dmatch.group(2))
-        else:
-          amatch = self.a_command.match(command)
-          if amatch:
-            count = string.atoi(amatch.group(2))
-            added = added + count
-            idx = idx + count
-          elif command:
-            raise "error while parsing deltatext: %s" % command
-
-    if len(rev.number) == 2:
-      rev.next_changed = changed and "+%i -%i" % (deled, added)
-    else:
-      rev.changed = changed and "+%i -%i" % (added, deled)
-
-class StreamText:
-  d_command = re.compile('^d(\d+)\\s(\\d+)')
-  a_command = re.compile('^a(\d+)\\s(\\d+)')
-
-  def __init__(self, text):
-    self.text = string.split(text, "\n")
-
-  def command(self, cmd):
-    adjust = 0
-    add_lines_remaining = 0
-    diffs = string.split(cmd, "\n")
-    if diffs[-1] == "":
-      del diffs[-1]
-    if len(diffs) == 0:
-      return
-    if diffs[0] == "":
-      del diffs[0]
-    for command in diffs:
-      if add_lines_remaining > 0:
-        # Insertion lines from a prior "a" command
-        self.text.insert(start_line + adjust, command)
-        add_lines_remaining = add_lines_remaining - 1
-        adjust = adjust + 1
-        continue
-      dmatch = self.d_command.match(command)
-      amatch = self.a_command.match(command)
-      if dmatch:
-        # "d" - Delete command
-        start_line = string.atoi(dmatch.group(1))
-        count      = string.atoi(dmatch.group(2))
-        begin = start_line + adjust - 1
-        del self.text[begin:begin + count]
-        adjust = adjust - count
-      elif amatch:
-        # "a" - Add command
-        start_line = string.atoi(amatch.group(1))
-        count      = string.atoi(amatch.group(2))
-        add_lines_remaining = count
-      else:
-        raise RuntimeError, 'Error parsing diff commands'
-
-def secondnextdot(s, start):
-  # find the position the second dot after the start index.
-  return string.find(s, '.', string.find(s, '.', start) + 1)
-
-
-class COSink(MatchingSink):
-  def __init__(self, rev):
-    MatchingSink.__init__(self, rev)
-
-  def set_head_revision(self, revision):
-    self.head = Revision(revision)
-    self.last = None
-    self.sstext = None
-
-  def admin_completed(self):
-    MatchingSink.admin_completed(self)
-    if self.find_tag is None:
-      raise vclib.InvalidRevision(self.find)
-
-  def set_revision_info(self, revision, log, text):
-    tag = self.find_tag
-    rev = Revision(revision)
+import os.path
 
-    if rev.number == tag.number:
-      self.log = log
 
-    depth = len(rev.number)
+def canonicalize_rootpath(rootpath):
+  return os.path.normpath(rootpath)
 
-    if rev.number == self.head.number:
-      assert self.sstext is None
-      self.sstext = StreamText(text)
-    elif (depth == 2 and tag.number and rev.number >= tag.number[:depth]):
-      assert len(self.last.number) == 2
-      assert rev.number < self.last.number
-      self.sstext.command(text)
-    elif (depth > 2 and rev.number[:depth-1] == tag.number[:depth-1] and
-          (rev.number <= tag.number or len(tag.number) == depth-1)):
-      assert len(rev.number) - len(self.last.number) in (0, 2)
-      assert rev.number > self.last.number
-      self.sstext.command(text)
-    else:
-      rev = None
 
-    if rev:
-      #print "tag =", tag.number, "rev =", rev.number, "<br>"
-      self.last = rev
+def expand_root_parent(parent_path):
+  # Each subdirectory of PARENT_PATH that contains a child
+  # "CVSROOT/config" is added the set of returned roots.  Or, if the
+  # PARENT_PATH itself contains a child "CVSROOT/config", then all its
+  # subdirectories are returned as roots.
+  roots = {}
+  subpaths = os.listdir(parent_path)
+  cvsroot = os.path.exists(os.path.join(parent_path, "CVSROOT", "config"))
+  for rootname in subpaths:
+    rootpath = os.path.join(parent_path, rootname)
+    if cvsroot \
+       or (os.path.exists(os.path.join(rootpath, "CVSROOT", "config"))):
+      roots[rootname] = canonicalize_rootpath(rootpath)
+  return roots
+
+
+def CVSRepository(name, rootpath, authorizer, utilities, use_rcsparse):
+  rootpath = canonicalize_rootpath(rootpath)
+  if use_rcsparse:
+    import ccvs
+    return ccvs.CCVSRepository(name, rootpath, authorizer, utilities)
+  else:
+    import bincvs
+    return bincvs.BinCVSRepository(name, rootpath, authorizer, utilities)

Modified: trunk/lib/vclib/ccvs/blame.py
==============================================================================
--- trunk/lib/vclib/ccvs/blame.py	(original)
+++ trunk/lib/vclib/ccvs/blame.py	Mon Nov  3 09:57:02 2008
@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 # -*-python-*-
 #
-# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
+# Copyright (C) 1999-2007 The ViewCVS Group. All Rights Reserved.
 # Copyright (C) 2000 Curt Hagenlocher <curt hagenlocher org>
 #
 # By using this file, you agree to the terms and conditions set forth in

Modified: trunk/lib/vclib/ccvs/rcsparse/common.py
==============================================================================
--- trunk/lib/vclib/ccvs/rcsparse/common.py	(original)
+++ trunk/lib/vclib/ccvs/rcsparse/common.py	Mon Nov  3 09:57:02 2008
@@ -1,6 +1,6 @@
 # -*-python-*-
 #
-# Copyright (C) 1999-2006 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
 # the LICENSE.html file which can be found at the top level of the ViewVC

Modified: trunk/lib/vclib/ccvs/rcsparse/default.py
==============================================================================
--- trunk/lib/vclib/ccvs/rcsparse/default.py	(original)
+++ trunk/lib/vclib/ccvs/rcsparse/default.py	Mon Nov  3 09:57:02 2008
@@ -1,6 +1,6 @@
 # -*-python-*-
 #
-# Copyright (C) 1999-2006 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
 # the LICENSE.html file which can be found at the top level of the ViewVC

Modified: trunk/lib/vclib/ccvs/rcsparse/texttools.py
==============================================================================
--- trunk/lib/vclib/ccvs/rcsparse/texttools.py	(original)
+++ trunk/lib/vclib/ccvs/rcsparse/texttools.py	Mon Nov  3 09:57:02 2008
@@ -1,6 +1,6 @@
 # -*-python-*-
 #
-# Copyright (C) 1999-2006 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
 # the LICENSE.html file which can be found at the top level of the ViewVC

Modified: trunk/lib/vclib/svn/__init__.py
==============================================================================
--- trunk/lib/vclib/svn/__init__.py	(original)
+++ trunk/lib/vclib/svn/__init__.py	Mon Nov  3 09:57:02 2008
@@ -1,6 +1,6 @@
 # -*-python-*-
 #
-# Copyright (C) 1999-2006 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
 # the LICENSE.html file which can be found at the top level of the ViewVC
@@ -10,657 +10,46 @@
 #
 # -----------------------------------------------------------------------
 
-"Version Control lib driver for locally accessible Subversion repositories"
+"Version Control lib driver for Subversion repositories"
 
-import vclib
 import os
 import os.path
-import string
-import cStringIO
-import signal
-import time
-import tempfile
-import popen
 import re
-from svn import fs, repos, core, client, delta
 
+_re_url = re.compile('^(http|https|file|svn|svn\+[^:]+)://')
 
-### Require Subversion 1.3.1 or better.
-if (core.SVN_VER_MAJOR, core.SVN_VER_MINOR, core.SVN_VER_PATCH) < (1, 3, 1):
-  raise Exception, "Version requirement not met (needs 1.3.1 or better)"
-
-  
-def _allow_all(root, path, pool):
-  """Generic authz_read_func that permits access to all paths"""
-  return 1
-
-
-def _fs_path_join(base, relative):
-  # Subversion filesystem paths are '/'-delimited, regardless of OS.
-  joined_path = base + '/' + relative
-  parts = filter(None, string.split(joined_path, '/'))
-  return string.join(parts, '/')
-
-
-def _cleanup_path(path):
-  """Return a cleaned-up Subversion filesystem path"""
-  return string.join(filter(None, string.split(path, '/')), '/')
-  
-
-def _compare_paths(path1, path2):
-  path1_len = len (path1);
-  path2_len = len (path2);
-  min_len = min(path1_len, path2_len)
-  i = 0
-
-  # Are the paths exactly the same?
-  if path1 == path2:
-    return 0
-  
-  # Skip past common prefix
-  while (i < min_len) and (path1[i] == path2[i]):
-    i = i + 1
-
-  # Children of paths are greater than their parents, but less than
-  # greater siblings of their parents
-  char1 = '\0'
-  char2 = '\0'
-  if (i < path1_len):
-    char1 = path1[i]
-  if (i < path2_len):
-    char2 = path2[i]
-    
-  if (char1 == '/') and (i == path2_len):
-    return 1
-  if (char2 == '/') and (i == path1_len):
-    return -1
-  if (i < path1_len) and (char1 == '/'):
-    return -1
-  if (i < path2_len) and (char2 == '/'):
-    return 1
-
-  # Common prefix was skipped above, next character is compared to
-  # determine order
-  return cmp(char1, char2)
-
-
-def _rev2optrev(rev):
-  assert type(rev) is int
-  rt = core.svn_opt_revision_t()
-  rt.kind = core.svn_opt_revision_number
-  rt.value.number = rev
-  return rt
-
-
-def _rootpath2url(rootpath, path):
-  rootpath = os.path.abspath(rootpath)
-  if rootpath and rootpath[0] != '/':
-    rootpath = '/' + rootpath
-  if os.sep != '/':
-    rootpath = string.replace(rootpath, os.sep, '/')
-  return 'file://' + string.join([rootpath, path], "/")
-
-
-def _datestr_to_date(datestr):
+def canonicalize_rootpath(rootpath):
   try:
-    return core.svn_time_from_cstring(datestr) / 1000000
+    import svn.core
+    return svn.core.svn_path_canonicalize(rootpath)
   except:
-    return None
-
-  
-def _fs_rev_props(fsptr, rev):
-  author = fs.revision_prop(fsptr, rev, core.SVN_PROP_REVISION_AUTHOR)
-  msg = fs.revision_prop(fsptr, rev, core.SVN_PROP_REVISION_LOG)
-  date = fs.revision_prop(fsptr, rev, core.SVN_PROP_REVISION_DATE)
-  return date, author, msg
-
-
-def date_from_rev(svnrepos, rev):
-  if (rev < 0) or (rev > svnrepos.youngest):
-    raise vclib.InvalidRevision(rev)
-  return _datestr_to_date(fs.revision_prop(svnrepos.fs_ptr, rev,
-                                           core.SVN_PROP_REVISION_DATE))
-
-
-def get_location(svnrepos, path, rev, old_rev):
-  try:
-    results = repos.svn_repos_trace_node_locations(svnrepos.fs_ptr, path,
-                                                   rev, [old_rev], _allow_all)
-  except core.SubversionException, e:
-    if e.apr_err == core.SVN_ERR_FS_NOT_FOUND:
-      raise vclib.ItemNotFound(path)
-    raise
-
-  try:
-    old_path = results[old_rev]
-  except KeyError:
-    raise vclib.ItemNotFound(path)
-
-  return _cleanup_path(old_path)
-
+    if re.search(_re_url, rootpath):
+      return rootpath[-1] == '/' and rootpath[:-1] or rootpath
+    return os.path.normpath(rootpath)
 
-def last_rev(svnrepos, path, peg_revision, limit_revision=None):
-  """Given PATH, known to exist in PEG_REVISION, find the youngest
-  revision older than, or equal to, LIMIT_REVISION in which path
-  exists.  Return that revision, and the path at which PATH exists in
-  that revision."""
-  
-  # Here's the plan, man.  In the trivial case (where PEG_REVISION is
-  # the same as LIMIT_REVISION), this is a no-brainer.  If
-  # LIMIT_REVISION is older than PEG_REVISION, we can use Subversion's
-  # history tracing code to find the right location.  If, however,
-  # LIMIT_REVISION is younger than PEG_REVISION, we suffer from
-  # Subversion's lack of forward history searching.  Our workaround,
-  # ugly as it may be, involves a binary search through the revisions
-  # between PEG_REVISION and LIMIT_REVISION to find our last live
-  # revision.
-  peg_revision = svnrepos._getrev(peg_revision)
-  limit_revision = svnrepos._getrev(limit_revision)
-  try:
-    if peg_revision == limit_revision:
-      return peg_revision, path
-    elif peg_revision > limit_revision:
-      fsroot = svnrepos._getroot(peg_revision)
-      history = fs.node_history(fsroot, path)
-      while history:
-        path, peg_revision = fs.history_location(history)
-        if peg_revision <= limit_revision:
-          return max(peg_revision, limit_revision), _cleanup_path(path)
-        history = fs.history_prev(history, 1)
-      return peg_revision, _cleanup_path(path)
-    else:
-      orig_id = fs.node_id(svnrepos._getroot(peg_revision), path)
-      while peg_revision != limit_revision:
-        mid = (peg_revision + 1 + limit_revision) / 2
-        try:
-          mid_id = fs.node_id(svnrepos._getroot(mid), path)
-        except core.SubversionException, e:
-          if e.apr_err == core.SVN_ERR_FS_NOT_FOUND:
-            cmp = -1
-          else:
-            raise
-        else:
-          ### Not quite right.  Need a comparison function that only returns
-          ### true when the two nodes are the same copy, not just related.
-          cmp = fs.compare_ids(orig_id, mid_id)
-
-        if cmp in (0, 1):
-          peg_revision = mid
-        else:
-          limit_revision = mid - 1
 
-      return peg_revision, path
-  finally:
+def expand_root_parent(parent_path):
+  roots = {}
+  if re.search(_re_url, parent_path):
     pass
-
-
-def created_rev(svnrepos, full_name, rev):
-  return fs.node_created_rev(svnrepos._getroot(rev), full_name)
-
-
-class Revision(vclib.Revision):
-  "Hold state for each revision's log entry."
-  def __init__(self, rev, date, author, msg, size,
-               filename, copy_path, copy_rev):
-    vclib.Revision.__init__(self, rev, str(rev), date, author, None, msg, size)
-    self.filename = filename
-    self.copy_path = copy_path
-    self.copy_rev = copy_rev
-
-
-class NodeHistory:
-  def __init__(self, fs_ptr, show_all_logs):
-    self.histories = {}
-    self.fs_ptr = fs_ptr
-    self.show_all_logs = show_all_logs
-    
-  def add_history(self, path, revision, pool):
-    # If filtering, only add the path and revision to the histories
-    # list if they were actually changed in this revision (where
-    # change means the path itself was changed, or one of its parents
-    # was copied).  This is useful for omitting bubble-up directory
-    # changes.
-    if not self.show_all_logs:
-      rev_root = fs.revision_root(self.fs_ptr, revision)
-      changed_paths = fs.paths_changed(rev_root)
-      paths = changed_paths.keys()
-      if path not in paths:
-        # Look for a copied parent
-        test_path = path
-        found = 0
-        while 1:
-          off = string.rfind(test_path, '/')
-          if off < 0:
-            break
-          test_path = test_path[0:off]
-          if test_path in paths:
-            copyfrom_rev, copyfrom_path = fs.copied_from(rev_root, test_path)
-            if copyfrom_rev >= 0 and copyfrom_path:
-              found = 1
-              break
-        if not found:
-          return
-    self.histories[revision] = _cleanup_path(path)
-    
-  
-def _get_history(svnrepos, full_name, rev, options={}):
-  fsroot = svnrepos._getroot(rev)
-  show_all_logs = options.get('svn_show_all_dir_logs', 0)
-  if not show_all_logs:
-    # See if the path is a file or directory.
-    kind = fs.check_path(fsroot, full_name)
-    if kind is core.svn_node_file:
-      show_all_logs = 1
-      
-  # Instantiate a NodeHistory collector object.
-  history = NodeHistory(svnrepos.fs_ptr, show_all_logs)
-
-  # Do we want to cross copy history?
-  cross_copies = options.get('svn_cross_copies', 0)
-
-  # Get the history items for PATH.
-  repos.svn_repos_history(svnrepos.fs_ptr, full_name, history.add_history,
-                          1, rev, cross_copies)
-  return history.histories
-
-
-def _log_helper(svnrepos, rev, path):
-  rev_root = fs.revision_root(svnrepos.fs_ptr, rev)
-
-  # Was this path rev the target of a copy?
-  copyfrom_rev, copyfrom_path = fs.copied_from(rev_root, path)
-
-  # Assemble our LogEntry
-  datestr, author, msg = _fs_rev_props(svnrepos.fs_ptr, rev)
-  date = _datestr_to_date(datestr)
-  if fs.is_file(rev_root, path):
-    size = fs.file_length(rev_root, path)
   else:
-    size = None
-  entry = Revision(rev, date, author, msg, size, path,
-                   copyfrom_path and _cleanup_path(copyfrom_path),
-                   copyfrom_rev)
-  return entry
-  
-
-def _fetch_log(svnrepos, full_name, which_rev, options):
-  revs = []
-
-  if options.get('svn_latest_log', 0):
-    rev = _log_helper(svnrepos, which_rev, full_name)
-    if rev:
-      revs.append(rev)
+    # Any subdirectories of PARENT_PATH which themselves have a child
+    # "format" are returned as roots.
+    subpaths = os.listdir(parent_path)
+    for rootname in subpaths:
+      rootpath = os.path.join(parent_path, rootname)
+      if os.path.exists(os.path.join(rootpath, "format")):
+        roots[rootname] = canonicalize_rootpath(rootpath)
+  return roots
+
+
+def SubversionRepository(name, rootpath, authorizer, utilities, config_dir):
+  rootpath = canonicalize_rootpath(rootpath)
+  if re.search(_re_url, rootpath):
+    import svn_ra
+    return svn_ra.RemoteSubversionRepository(name, rootpath, authorizer,
+                                             utilities, config_dir)
   else:
-    history_set = _get_history(svnrepos, full_name, which_rev, options)
-    history_revs = history_set.keys()
-    history_revs.sort()
-    history_revs.reverse()
-    for history_rev in history_revs:
-      rev = _log_helper(svnrepos, history_rev, history_set[history_rev])
-      if rev:
-        revs.append(rev)
-  return revs
-
-def _get_last_history_rev(fsroot, path):
-  history = fs.node_history(fsroot, path)
-  history = fs.history_prev(history, 0)
-  history_path, history_rev = fs.history_location(history)
-  return history_rev
-  
-def get_youngest_revision(svnrepos):
-  return svnrepos.youngest
-
-def temp_checkout(svnrepos, path, rev):
-  """Check out file revision to temporary file"""
-  temp = tempfile.mktemp()
-  fp = open(temp, 'wb')
-  try:
-    root = svnrepos._getroot(rev)
-    stream = fs.file_contents(root, path)
-    try:
-      while 1:
-        chunk = core.svn_stream_read(stream, core.SVN_STREAM_CHUNK_SIZE)
-        if not chunk:
-          break
-        fp.write(chunk)
-    finally:
-      core.svn_stream_close(stream)
-  finally:
-    fp.close()
-  return temp
-
-class FileContentsPipe:
-  def __init__(self, root, path):
-    self._stream = fs.file_contents(root, path)
-    self._eof = 0
-
-  def read(self, len=None):
-    chunk = None
-    if not self._eof:
-      if len is None:
-        buffer = cStringIO.StringIO()
-        try:
-          while 1:
-            hunk = core.svn_stream_read(self._stream, 8192)
-            if not hunk:
-              break
-            buffer.write(hunk)
-          chunk = buffer.getvalue()
-        finally:
-          buffer.close()
-
-      else:
-        chunk = core.svn_stream_read(self._stream, len)   
-    if not chunk:
-      self._eof = 1
-    return chunk
-  
-  def readline(self):
-    chunk = None
-    if not self._eof:
-      chunk, self._eof = core.svn_stream_readline(self._stream, '\n')
-      if not self._eof:
-        chunk = chunk + '\n'
-    if not chunk:
-      self._eof = 1
-    return chunk
-
-  def readlines(self):
-    lines = []
-    while True:
-      line = self.readline()
-      if not line:
-        break
-      lines.append(line)
-    return lines
-
-  def close(self):
-    return core.svn_stream_close(self._stream)
-
-  def eof(self):
-    return self._eof
-
-
-class BlameSource:
-  def __init__(self, local_url, rev, first_rev):
-    self.idx = -1
-    self.first_rev = first_rev
-    self.blame_data = []
-
-    ctx = client.ctx_t()
-    core.svn_config_ensure(None)
-    ctx.config = core.svn_config_get_config(None)
-    ctx.auth_baton = core.svn_auth_open([])
-    try:
-      client.blame2(local_url, _rev2optrev(rev), _rev2optrev(1),
-                    _rev2optrev(rev), self._blame_cb, ctx)
-    except vclib.svn.core.SubversionException, e:
-      if e.apr_err == vclib.svn.core.SVN_ERR_CLIENT_IS_BINARY_FILE:
-        raise vclib.NonTextualFileContents
-      raise
-
-  def _blame_cb(self, line_no, rev, author, date, text, pool):
-    prev_rev = None
-    if rev > self.first_rev:
-      prev_rev = rev - 1
-    self.blame_data.append(vclib.Annotation(text, line_no, rev,
-                                            prev_rev, author, None))
-
-  def __getitem__(self, idx):
-    if idx != self.idx + 1:
-      raise BlameSequencingError()
-    self.idx = idx
-    return self.blame_data[idx]
-
-
-class BlameSequencingError(Exception):
-  pass
-
-
-class SVNChangedPath(vclib.ChangedPath):
-  """Wrapper around vclib.ChangedPath which handles path splitting."""
-  
-  def __init__(self, path, rev, pathtype, base_path, base_rev,
-               action, copied, text_changed, props_changed):
-    path_parts = filter(None, string.split(path or '', '/'))
-    base_path_parts = filter(None, string.split(base_path or '', '/'))
-    vclib.ChangedPath.__init__(self, path_parts, rev, pathtype,
-                               base_path_parts, base_rev, action,
-                               copied, text_changed, props_changed)
-
-  
-class SubversionRepository(vclib.Repository):
-  def __init__(self, name, rootpath, utilities):
-    if not os.path.isdir(rootpath):
-      raise vclib.ReposNotFound(name)
-
-    # Initialize some stuff.
-    self.rootpath = rootpath
-    self.name = name
-    self.svn_client_path = utilities.svn or 'svn'
-    self.diff_cmd = utilities.diff or 'diff'
-
-    # Register a handler for SIGTERM so we can have a chance to
-    # cleanup.  If ViewVC takes too long to start generating CGI
-    # output, Apache will grow impatient and SIGTERM it.  While we
-    # don't mind getting told to bail, we want to gracefully close the
-    # repository before we bail.
-    def _sigterm_handler(signum, frame, self=self):
-      self._close()
-      sys.exit(-1)
-    try:
-      signal.signal(signal.SIGTERM, _sigterm_handler)
-    except ValueError:
-      # This is probably "ValueError: signal only works in main
-      # thread", which will get thrown by the likes of mod_python
-      # when trying to install a signal handler from a thread that
-      # isn't the main one.  We'll just not care.
-      pass
-
-    # Open the repository and init some other variables.
-    self.repos = repos.svn_repos_open(rootpath)
-    self.fs_ptr = repos.svn_repos_fs(self.repos)
-    self.youngest = fs.youngest_rev(self.fs_ptr)
-    self._fsroots = {}
-
-  def rootname(self):
-    return self.name
-
-  def rootpath(self):
-    return self.rootpath
-
-  def roottype(self):
-    return vclib.SVN
-
-  def itemtype(self, path_parts, rev):
-    rev = self._getrev(rev)
-    basepath = self._getpath(path_parts)
-    kind = fs.check_path(self._getroot(rev), basepath)
-    if kind == core.svn_node_dir:
-      return vclib.DIR
-    if kind == core.svn_node_file:
-      return vclib.FILE
-    raise vclib.ItemNotFound(path_parts)
-
-  def openfile(self, path_parts, rev):
-    path = self._getpath(path_parts)
-    rev = self._getrev(rev)
-    fsroot = self._getroot(rev)
-    revision = str(_get_last_history_rev(fsroot, path))
-    fp = FileContentsPipe(fsroot, path)
-    return fp, revision
-
-  def listdir(self, path_parts, rev, options):
-    basepath = self._getpath(path_parts)
-    if self.itemtype(path_parts, rev) != vclib.DIR:
-      raise vclib.Error("Path '%s' is not a directory." % basepath)
-
-    rev = self._getrev(rev)
-    fsroot = self._getroot(rev)
-    dirents = fs.dir_entries(fsroot, basepath)
-    entries = [ ]
-    for entry in dirents.values():
-      if entry.kind == core.svn_node_dir:
-        kind = vclib.DIR
-      elif entry.kind == core.svn_node_file:
-        kind = vclib.FILE              
-      entries.append(vclib.DirEntry(entry.name, kind))
-    return entries
-
-  def dirlogs(self, path_parts, rev, entries, options):
-    fsroot = self._getroot(self._getrev(rev))
-    for entry in entries:
-      path = self._getpath(path_parts + [entry.name])
-      rev = _get_last_history_rev(fsroot, path)
-      datestr, author, msg = _fs_rev_props(self.fs_ptr, rev)
-      date = _datestr_to_date(datestr)
-      entry.rev = str(rev)
-      entry.date = date
-      entry.author = author
-      entry.log = msg
-      if entry.kind == vclib.FILE:
-        entry.size = fs.file_length(fsroot, path)
-
-  def itemlog(self, path_parts, rev, options):
-    """see vclib.Repository.itemlog docstring
-
-    Option values recognized by this implementation
-
-      svn_show_all_dir_logs
-        boolean, default false. if set for a directory path, will include
-        revisions where files underneath the directory have changed
-
-      svn_cross_copies
-        boolean, default false. if set for a path created by a copy, will
-        include revisions from before the copy
-
-      svn_latest_log
-        boolean, default false. if set will return only newest single log
-        entry
-    """
-    path = self._getpath(path_parts)
-    rev = self._getrev(rev)
-
-    revs = _fetch_log(self, path, rev, options)
-    revs.sort()
-    prev = None
-    for rev in revs:
-      rev.prev = prev
-      prev = rev
-    return revs
-
-  def annotate(self, path_parts, rev):
-    path = self._getpath(path_parts)
-    rev = self._getrev(rev)
-    fsroot = self._getroot(rev)
-
-    history_set = _get_history(self, path, rev, {'svn_cross_copies': 1})
-    history_revs = history_set.keys()
-    history_revs.sort()
-    revision = history_revs[-1]
-    first_rev = history_revs[0]
-    source = BlameSource(_rootpath2url(self.rootpath, path), rev, first_rev)
-    return source, revision
-
-  def revinfo(self, rev):
-    fsroot = self._getroot(rev)
-
-    # Get the changes for the revision
-    editor = repos.ChangeCollector(self.fs_ptr, fsroot)
-    e_ptr, e_baton = delta.make_editor(editor)
-    repos.svn_repos_replay(fsroot, e_ptr, e_baton)
-    changes = editor.get_changes()
-    changedpaths = {}
-    
-    # Now get the revision property info.  Would use
-    # editor.get_root_props(), but something is broken there...
-    datestr, author, msg = _fs_rev_props(self.fs_ptr, rev)
-
-    # Copy the Subversion changes into a new hash, converting them into
-    # ChangedPath objects.
-    for path in changes.keys():
-      change = changes[path]
-      if change.path:
-        change.path = _cleanup_path(change.path)
-      if change.base_path:
-        change.base_path = _cleanup_path(change.base_path)
-      is_copy = 0
-      if not hasattr(change, 'action'): # new to subversion 1.4.0
-        action = vclib.MODIFIED
-        if not change.path:
-          action = vclib.DELETED
-        elif change.added:
-          action = vclib.ADDED
-          replace_check_path = path
-          if change.base_path and change.base_rev:
-            replace_check_path = change.base_path
-          if changedpaths.has_key(replace_check_path) \
-             and changedpaths[replace_check_path].action == vclib.DELETED:
-            action = vclib.REPLACED
-      else:
-        if change.action == repos.CHANGE_ACTION_ADD:
-          action = vclib.ADDED
-        elif change.action == repos.CHANGE_ACTION_DELETE:
-          action = vclib.DELETED
-        elif change.action == repos.CHANGE_ACTION_REPLACE:
-          action = vclib.REPLACED
-        else:
-          action = vclib.MODIFIED
-      if (action == vclib.ADDED or action == vclib.REPLACED) \
-         and change.base_path \
-         and change.base_rev:
-        is_copy = 1
-      if change.item_kind == core.svn_node_dir:
-        pathtype = vclib.DIR
-      elif change.item_kind == core.svn_node_file:
-        pathtype = vclib.FILE
-      else:
-        pathtype = None
-      
-      changedpaths[path] = SVNChangedPath(path, rev, pathtype,
-                                          change.base_path,
-                                          change.base_rev, action,
-                                          is_copy, change.text_changed,
-                                          change.prop_changes)
-    
-    # Return our tuple: date, author, msg, changes
-    return _datestr_to_date(datestr), author, msg, changedpaths.values()
-
-  def rawdiff(self, path_parts1, rev1, path_parts2, rev2, type, options={}):
-    p1 = self._getpath(path_parts1)
-    p2 = self._getpath(path_parts2)
-    r1 = self._getrev(rev1)
-    r2 = self._getrev(rev2)
-    args = vclib._diff_args(type, options)
-
-    try:
-      temp1 = temp_checkout(self, p1, r1)
-      temp2 = temp_checkout(self, p2, r2)
-      info1 = p1, date_from_rev(self, r1), r1
-      info2 = p2, date_from_rev(self, r2), r2
-      return vclib._diff_fp(temp1, temp2, info1, info2, self.diff_cmd, args)
-    except vclib.svn.core.SubversionException, e:
-      if e.apr_err == vclib.svn.core.SVN_ERR_FS_NOT_FOUND:
-        raise vclib.InvalidRevision
-      raise
-
-  def _getpath(self, path_parts):
-    return string.join(path_parts, '/')
-
-  def _getrev(self, rev):
-    if rev is None or rev == 'HEAD':
-      return self.youngest
-    try:
-      rev = int(rev)
-    except ValueError:
-      raise vclib.InvalidRevision(rev)
-    if (rev < 0) or (rev > self.youngest):
-      raise vclib.InvalidRevision(rev)
-    return rev
-
-  def _getroot(self, rev):
-    try:
-      return self._fsroots[rev]
-    except KeyError:
-      r = self._fsroots[rev] = fs.revision_root(self.fs_ptr, rev)
-      return r
+    import svn_repos
+    return svn_repos.LocalSubversionRepository(name, rootpath, authorizer,
+                                               utilities, config_dir)

Modified: trunk/lib/viewvc.py
==============================================================================
--- trunk/lib/viewvc.py	(original)
+++ trunk/lib/viewvc.py	Mon Nov  3 09:57:02 2008
@@ -1,6 +1,6 @@
 # -*-python-*-
 #
-# Copyright (C) 1999-2007 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
 # the LICENSE.html file which can be found at the top level of the ViewVC
@@ -14,7 +14,7 @@
 #
 # -----------------------------------------------------------------------
 
-__version__ = '1.1-dev'
+__version__ = '1.2-dev'
 
 # this comes from our library; measure the startup time
 import debug
@@ -24,27 +24,30 @@
 # standard modules that we know are in the path or builtin
 import sys
 import os
-import sapi
 import cgi
-import string
-import urllib
+import gzip
 import mimetypes
-import time
 import re
 import rfc822
 import stat
+import string
 import struct
-import types
 import tempfile
+import time
+import types
+import urllib
 
-# these modules come from our library (the stub has set up the path)
+# These modules come from our library (the stub has set up the path)
+import accept
 import compat
 import config
-import popen
 import ezt
-import accept
-import vclib
+import popen
+import sapi
 import vcauth
+import vclib
+import vclib.ccvs
+import vclib.svn
 
 try:
   import idiff
@@ -77,6 +80,10 @@
   'limit_changes',
   ]
 
+# number of extra pages of information on either side of the current
+# page to fetch (see use_pagesize configuration option)
+EXTRA_PAGES = 3
+
 # for reading/writing between a couple descriptors
 CHUNK_SIZE = 8192
 
@@ -96,40 +103,29 @@
     self.script_name = _normalize_path(server.getenv('SCRIPT_NAME', ''))
     self.browser = server.getenv('HTTP_USER_AGENT', 'unknown')
 
-    # in lynx, it it very annoying to have two links per file, so
-    # disable the link at the icon in this case:
-    self.no_file_links = string.find(self.browser, 'Lynx') != -1
-
-    # newer browsers accept gzip content encoding and state this in a
-    # header (netscape did always but didn't state it) It has been
-    # reported that these braindamaged MS-Internet Explorers claim
-    # that they accept gzip .. but don't in fact and display garbage
-    # then :-/
-    self.may_compress = (
-      ( string.find(server.getenv('HTTP_ACCEPT_ENCODING', ''), 'gzip') != -1
-        or string.find(self.browser, 'Mozilla/3') != -1)
-      and string.find(self.browser, 'MSIE') == -1
-      )
-
-    # process the Accept-Language: header
+    # process the Accept-Language: header, and load the key/value
+    # files, given the selected language
     hal = server.getenv('HTTP_ACCEPT_LANGUAGE','')
     self.lang_selector = accept.language(hal)
     self.language = self.lang_selector.select_from(cfg.general.languages)
+    self.kv = cfg.load_kv_files(self.language)
 
     # check for an authenticated username
     self.username = server.getenv('REMOTE_USER')
 
-    # load the key/value files, given the selected language
-    self.kv = cfg.load_kv_files(self.language)
+    # if we allow compressed output, see if the client does too
+    self.gzip_compress_level = 0
+    if cfg.options.allow_compress:
+      http_accept_encoding = os.environ.get("HTTP_ACCEPT_ENCODING", "")
+      if "gzip" in filter(None,
+                          map(lambda x: string.strip(x),
+                              string.split(http_accept_encoding, ","))):
+        self.gzip_compress_level = 9  # make this configurable?
 
   def run_viewvc(self):
 
     cfg = self.cfg
 
-    # global needed because "import vclib.svn" causes the
-    # interpreter to make vclib a local variable
-    global vclib
-
     # This function first parses the query string and sets the following
     # variables. Then it executes the request.
     self.view_func = None  # function to call to process the request
@@ -144,9 +140,6 @@
     self.pathrev = None    # current path revision or tag
     self.auth = None       # authorizer module in use
 
-    # setup the default authorizer (until we have a root)
-    self.auth = vcauth.ViewVCAuthorizer()
-    
     # redirect if we're loading from a valid but irregular URL
     # These redirects aren't neccessary to make ViewVC work, it functions
     # just fine without them, but they make it easier for server admins to
@@ -168,6 +161,15 @@
       # validate the parameter
       _validate_param(name, values[0])
 
+      # Only allow the magic ViewVC MIME types (the ones used for
+      # requesting the markup as as-text views) to be declared via CGI
+      # params.  Ignore disallowed values.
+      if (name == 'content-type') and \
+         (not values[0] in (viewcvs_mime_type,
+                            alt_mime_type,
+                            'text/plain')):
+        continue
+      
       # if we're here, then the parameter is okay
       self.query_dict[name] = values[0]
 
@@ -218,7 +220,7 @@
           self.rootname = path_parts.pop(0)
         else:
           self.rootname = ""
-      else:
+      elif self.view_func != view_roots:
         self.rootname = cfg.general.default_root
     elif cfg.options.root_as_url_component:
       needs_redirect = 1
@@ -227,59 +229,45 @@
     self.path_parts = path_parts
 
     if self.rootname:
-      # Create the repository object
-      if cfg.general.cvs_roots.has_key(self.rootname):
-        cfg.overlay_root_options(self.rootname)
-        self.rootpath = os.path.normpath(cfg.general.cvs_roots[self.rootname])
-        try:
-          if cfg.options.use_rcsparse:
-            import vclib.ccvs
-            self.repos = vclib.ccvs.CCVSRepository(self.rootname,
-                                                   self.rootpath,
-                                                   cfg.utilities)
-          else:
-            import vclib.bincvs
-            self.repos = vclib.bincvs.BinCVSRepository(self.rootname, 
-                                                       self.rootpath,
-                                                       cfg.utilities)
-        except vclib.ReposNotFound:
-          raise debug.ViewVCException(
-            'Unable to locate CVS root "%s".  Possible causes include '
-            'having an incorrectly configured path for this root, or the '
-            'server on which the repository lives being inaccessible.' \
-            % self.rootname)
-        # required so that spawned rcs programs correctly expand $CVSHeader$
-        os.environ['CVSROOT'] = self.rootpath
-      elif cfg.general.svn_roots.has_key(self.rootname):
+      roottype, rootpath = locate_root(cfg, self.rootname)
+      if roottype:
+        # Overlay root-specific options.
         cfg.overlay_root_options(self.rootname)
-        self.rootpath = cfg.general.svn_roots[self.rootname]
+        
+        # Setup an Authorizer for this rootname and username
+        self.auth = setup_authorizer(cfg, self.username, self.rootname)
+
+        # Create the repository object
         try:
-          if re.match(_re_rewrite_url, self.rootpath):
-            # If the rootpath is a URL, we'll use the svn_ra module, but
-            # lie about its name.
-            import vclib.svn_ra
-            vclib.svn = vclib.svn_ra
+          if roottype == 'cvs':
+            self.rootpath = vclib.ccvs.canonicalize_rootpath(rootpath)
+            self.repos = vclib.ccvs.CVSRepository(self.rootname,
+                                                  self.rootpath,
+                                                  self.auth,
+                                                  cfg.utilities,
+                                                  cfg.options.use_rcsparse)
+            # required so that spawned rcs programs correctly expand
+            # $CVSHeader$
+            os.environ['CVSROOT'] = self.rootpath
+          elif roottype == 'svn':
+            self.rootpath = vclib.svn.canonicalize_rootpath(rootpath)
+            self.repos = vclib.svn.SubversionRepository(self.rootname,
+                                                        self.rootpath,
+                                                        self.auth,
+                                                        cfg.utilities,
+                                                        cfg.options.svn_config_dir)
           else:
-            self.rootpath = os.path.normpath(self.rootpath)
-            import vclib.svn
-          self.repos = vclib.svn.SubversionRepository(self.rootname,
-                                                      self.rootpath,
-                                                      cfg.utilities)
+            raise vclib.ReposNotFound()
         except vclib.ReposNotFound:
-          raise debug.ViewVCException(
-            'Unable to locate Subversion root "%s".  Possible causes include '
-            'having an incorrectly configured path for this root, or the '
-            'server on which the repository lives being inaccessible.' \
-            % self.rootname)
-        except vclib.InvalidRevision, ex:
-          raise debug.ViewVCException(str(ex))
-      else:
+          pass
+      if self.repos is None:
         raise debug.ViewVCException(
           'The root "%s" is unknown. If you believe the value is '
           'correct, then please double-check your configuration.'
-          % self.rootname, "404 Repository not found")
+          % self.rootname, "404 Not Found")
 
     if self.repos:
+      self.repos.open()
       type = self.repos.roottype()
       if type == vclib.SVN:
         self.roottype = 'svn'
@@ -290,9 +278,6 @@
           'The root "%s" has an unknown type (%s).' % (self.rootname, type),
           "500 Internal Server Error")
       
-    if self.rootname and cfg.options.authorizer:
-      self.auth = setup_authorizer(cfg, self.username, self.repos)
-
     # If this is using an old-style 'rev' parameter, redirect to new hotness.
     # Subversion URLs will now use 'pathrev'; CVS ones use 'revision'.
     if self.repos and self.query_dict.has_key('rev'):
@@ -307,37 +292,43 @@
       needs_redirect = 1
 
     if self.repos and self.view_func is not redirect_pathrev:
+      # If this is an intended-to-be-hidden CVSROOT path, complain.
+      if cfg.options.hide_cvsroot \
+         and is_cvsroot_path(self.roottype, path_parts):
+        raise debug.ViewVCException("Unknown location: /%s" % self.where,
+                                    "404 Not Found")
+
       # Make sure path exists
       self.pathrev = pathrev = self.query_dict.get('pathrev')
       self.pathtype = _repos_pathtype(self.repos, path_parts, pathrev)
 
       if self.pathtype is None:
-        # path doesn't exist, see if it could be an old-style ViewVC URL
-        # with a fake suffix
-        result = _strip_suffix('.diff', path_parts, pathrev, vclib.FILE,      \
-                               self.repos, view_diff) or                      \
-                 _strip_suffix('.tar.gz', path_parts, pathrev, vclib.DIR,     \
-                               self.repos, download_tarball) or               \
-                 _strip_suffix('root.tar.gz', path_parts, pathrev, vclib.DIR, \
-                               self.repos, download_tarball) or               \
-                 _strip_suffix(self.rootname + '-root.tar.gz',                \
-                               path_parts, pathrev, vclib.DIR,                \
-                               self.repos, download_tarball) or               \
-                 _strip_suffix('root',                                        \
-                               path_parts, pathrev, vclib.DIR,                \
-                               self.repos, download_tarball) or               \
-                 _strip_suffix(self.rootname + '-root',                       \
-                               path_parts, pathrev, vclib.DIR,                \
+        # Path doesn't exist, see if it could be an old-style ViewVC URL
+        # with a fake suffix.
+        result = _strip_suffix('.diff', path_parts, pathrev, vclib.FILE,     \
+                               self.repos, view_diff) or                     \
+                 _strip_suffix('.tar.gz', path_parts, pathrev, vclib.DIR,    \
+                               self.repos, download_tarball) or              \
+                 _strip_suffix('root.tar.gz', path_parts, pathrev, vclib.DIR,\
+                               self.repos, download_tarball) or              \
+                 _strip_suffix(self.rootname + '-root.tar.gz',               \
+                               path_parts, pathrev, vclib.DIR,               \
+                               self.repos, download_tarball) or              \
+                 _strip_suffix('root',                                       \
+                               path_parts, pathrev, vclib.DIR,               \
+                               self.repos, download_tarball) or              \
+                 _strip_suffix(self.rootname + '-root',                      \
+                               path_parts, pathrev, vclib.DIR,               \
                                self.repos, download_tarball)
         if result:
           self.path_parts, self.pathtype, self.view_func = result
           self.where = _path_join(self.path_parts)
           needs_redirect = 1
         else:
-          raise debug.ViewVCException('%s: unknown location'
-                                       % self.where, '404 Not Found')
+          raise debug.ViewVCException("Unknown location: /%s" % self.where,
+                                      "404 Not Found")
 
-      # If we have an old ViewCVS Attic URL which is still valid, then redirect
+      # If we have an old ViewCVS Attic URL which is still valid, redirect
       if self.roottype == 'cvs':
         attic_parts = None
         if (self.pathtype == vclib.FILE and len(self.path_parts) > 1
@@ -351,13 +342,6 @@
           self.where = _path_join(attic_parts)
           needs_redirect = 1
 
-    # Check authorization for files and directories.
-    if self.path_parts:
-      if not self.auth.check_path_access(self.path_parts):
-        path = _path_join(self.path_parts)
-        raise debug.ViewVCNotAuthorizedException(self.username,
-                                                 'path "%s"' % (path))
-    
     if self.view_func is None:
       # view parameter is not set, try looking at pathtype and the 
       # other parameters
@@ -381,19 +365,26 @@
             self.view_func = view_cvsgraph_image
         elif self.query_dict.has_key('revision') \
                  or cfg.options.default_file_view != "log":
-          if self.query_dict.get('content-type', None) in (viewcvs_mime_type,
-                                                           alt_mime_type):
+          if cfg.options.default_file_view == "markup" \
+             or self.query_dict.get('content-type', None) \
+                 in (viewcvs_mime_type, alt_mime_type):
             self.view_func = view_markup
           else:
             self.view_func = view_checkout
         else:
           self.view_func = view_log
 
+    # If we've chosen the roots or revision view, our effective
+    # location is not really "inside" the repository, so we have no
+    # path and therefore no path parts or type, either.
+    if self.view_func is view_revision or self.view_func is view_roots:
+      self.where = ''
+      self.path_parts = []
+      self.pathtype = None
+      
     # if we have a directory and the request didn't end in "/", then redirect
     # so that it does.
     if (self.pathtype == vclib.DIR and path_info[-1:] != '/'
-        and self.view_func is not view_revision
-        and self.view_func is not view_roots
         and self.view_func is not download_tarball
         and self.view_func is not redirect_pathrev):
       needs_redirect = 1
@@ -402,14 +393,10 @@
     if needs_redirect:
       self.server.redirect(self.get_url())
 
-    # Finally done parsing query string, set mime type and call view_func
-    self.mime_type = None
-    if self.pathtype == vclib.FILE:
-      self.mime_type = guess_mime(self.where)
-
     # startup is done now.
     debug.t_end('startup')
     
+    # Call the function for the selected view.
     self.view_func(self)
 
   def get_url(self, escape=0, partial=0, prefix=0, **args):
@@ -438,13 +425,16 @@
 
   def get_form(self, **args):
     """Constructs a link to another ViewVC page just like the get_link
-    function except that it returns a base URL suitable for use as an HTML
-    form action and a string of HTML input type=hidden tags with the link
-    parameters."""
+    function except that it returns a base URL suitable for use as an
+    HTML form action, and an iterable object with .name and .value
+    attributes representing stuff that should be in <input
+    type=hidden> tags with the link parameters."""
 
     url, params = apply(self.get_link, (), args)
     action = self.server.escape(urllib.quote(url, _URL_SAFE_CHARS))
-    hidden_values = prepare_hidden_values(params)
+    hidden_values = []
+    for name, value in params.items():
+      hidden_values.append(_item(name=name, value=value))
     return action, hidden_values
 
   def get_link(self, view_func=None, where=None, pathtype=None, params=None):
@@ -526,15 +516,8 @@
     if where:
       url = url + '/' + where
 
-    # add suffix for tarball
-    if view_func is download_tarball:
-      if not where and not cfg.options.root_as_url_component:
-        url = url + '/' + rootname + '-root'
-        params['parent'] = '1'
-      url = url + '.tar.gz'
-
     # add trailing slash for a directory
-    elif pathtype == vclib.DIR:
+    if pathtype == vclib.DIR:
       url = url + '/'
 
     # normalize top level URLs for use in Location headers and A tags
@@ -563,13 +546,19 @@
       view_func = None
 
     # no need to explicitly specify checkout view when it's the default
-    # view, when checkout_magic is enabled, or when "revision" is present
+    # view or when checkout_magic is enabled
     if view_func is view_checkout:
-      if ((cfg.options.default_file_view != "log" and pathtype == vclib.FILE)
-          or cfg.options.checkout_magic
-          or params.get('revision') is not None):
+      if ((cfg.options.default_file_view == "co" and pathtype == vclib.FILE)
+          or cfg.options.checkout_magic):
         view_func = None
 
+    # no need to explicitly specify markup view when it's the default view
+    if view_func is view_markup:
+      if (cfg.options.default_file_view == "markup" \
+          and pathtype == vclib.FILE):
+        view_func = None
+
+    # set the view parameter
     view_code = _view_codes.get(view_func)
     if view_code and not (params.has_key('view') and params['view'] is None):
       params['view'] = view_code
@@ -662,7 +651,8 @@
 _re_validate_mimetype = re.compile('^[-_.a-zA-Z0-9/]+$')
 
 # date time values
-_re_validate_datetime = re.compile(r'^(\d\d\d\d-\d\d-\d\d(\s+\d\d:\d\d(:\d\d)?)?)?$')
+_re_validate_datetime = re.compile(r'^(\d\d\d\d-\d\d-\d\d(\s+\d\d:\d\d'
+                                   '(:\d\d)?)?)?$')
 
 # the legal query parameters and their validation functions
 _legal_params = {
@@ -681,17 +671,13 @@
   'pathrev'       : _re_validate_revnum,
   'dir_pagestart' : _re_validate_number,
   'log_pagestart' : _re_validate_number,
-  'hidecvsroot'   : _re_validate_number,
   'annotate'      : _re_validate_revnum,
   'graph'         : _re_validate_revnum,
   'makeimage'     : _re_validate_number,
-  'tarball'       : _re_validate_number,
-  'parent'        : _re_validate_number,
   'r1'            : _re_validate_revnum,
   'tr1'           : _re_validate_revnum,
   'r2'            : _re_validate_revnum,
   'tr2'           : _re_validate_revnum,
-  'rev'           : _re_validate_revnum,
   'revision'      : _re_validate_revnum,
   'content-type'  : _re_validate_mimetype,
 
@@ -718,6 +704,12 @@
   'orig_pathtype' : None,
   'orig_pathrev'  : None,
   'orig_view'     : None,
+
+  # deprecated
+  'parent'        : _re_validate_number,
+  'rev'           : _re_validate_revnum,
+  'tarball'       : _re_validate_number,
+  'hidecvsroot'   : _re_validate_number,
   }
 
 def _path_join(path_parts):
@@ -726,6 +718,8 @@
 def _strip_suffix(suffix, path_parts, rev, pathtype, repos, view_func):
   """strip the suffix from a repository path if the resulting path
   is of the specified type, otherwise return None"""
+  if not path_parts:
+    return None
   l = len(suffix)
   if path_parts[-1][-l:] == suffix:
     path_parts = path_parts[:]
@@ -739,7 +733,8 @@
   return None
 
 def _repos_pathtype(repos, path_parts, rev):
-  """return the type of a repository path, or None if the path doesn't exist"""
+  """Return the type of a repository path, or None if the path doesn't
+  exist"""
   try:
     return repos.itemtype(path_parts, rev)
   except vclib.ItemNotFound:
@@ -786,38 +781,42 @@
       rev = request.repos._getrev(rev)
     except vclib.InvalidRevision:
       raise debug.ViewVCException('Invalid revision', '404 Not Found')
-    return _path_parts(vclib.svn.get_location(request.repos, path, 
-                                              pathrev, rev)), rev
+    return _path_parts(request.repos.get_location(path, pathrev, rev)), rev
   return _path_parts(path), rev
 
-def setup_authorizer(cfg, username, root, params={}):
-  rootname = root.rootname()
+def setup_authorizer(cfg, username, rootname):
+  import imp
+  
+  # No configured authorizer?  No problem.
+  if not cfg.options.authorizer:
+    return None
 
   # First, try to load a module with the configured name.
+  fp = None
   try:
-    exec('import vcauth.%s' % (cfg.options.authorizer))
-  except ImportError:
-    raise debug.ViewVCException(
-      'Invalid authorizer (%s) specified for root "%s"' \
-      % (cfg.options.authorizer, rootname),
-      '500 Internal Server Error')
+    try:
+      fp, path, desc = imp.find_module("%s" % (cfg.options.authorizer),
+                                       vcauth.__path__)
+      my_auth = imp.load_module('viewvc', fp, path, desc)
+    except ImportError:
+      raise debug.ViewVCException(
+        'Invalid authorizer (%s) specified for root "%s"' \
+        % (cfg.options.authorizer, rootname),
+        '500 Internal Server Error')
+  finally:
+    if fp:
+      fp.close()
 
   # Now we'll get custom parameters for our particular root.
-  exec('my_auth = vcauth.%s' % (cfg.options.authorizer))
   params = cfg.get_authorizer_params(cfg.options.authorizer, rootname)
 
   # Finally, instantiate our Authorizer.
-  try:
-    return my_auth.ViewVCAuthorizer(username, root, params)
-  except vcauth.ViewVCRootAccessNotAuthorized:
-    raise debug.ViewVCNotAuthorizedException(username,
-                                             'root "%s"' % (rootname))
+  return my_auth.ViewVCAuthorizer(username, params)
 
 def check_freshness(request, mtime=None, etag=None, weak=0):
-  # See if we are supposed to disable etags (for debugging, usually)
-
   cfg = request.cfg
 
+  # See if we are supposed to disable etags (for debugging, usually)
   if not cfg.options.generate_etags:
     return 0
   
@@ -863,18 +862,18 @@
   return isfresh
 
 def get_view_template(cfg, view_name, language="en"):
-  # see if the configuration specifies a template for this view
-  tname = vars(cfg.templates).get(view_name)
+  # See if the configuration specifies a template for this view.  If
+  # not, use the default template path for this view.
+  tname = vars(cfg.templates).get(view_name) or view_name + ".ezt"
+
+  # Template paths are relative to the configurated template_dir (if
+  # any, "templates" otherwise), so build the template path as such.
+  tname = os.path.join(cfg.options.template_dir or "templates", tname)
 
-  # if there is no specific template definition for this view, look in
-  # the default location (relative to the configured template_dir)
-  if not tname:
-    tname = os.path.join(cfg.options.template_dir, view_name + ".ezt")
-
-  # allow per-language template selection
+  # Allow per-language template selection.
   tname = string.replace(tname, '%lang%', language)
 
-  # finally, construct the whole template path.
+  # Finally, construct the whole template path.
   tname = cfg.path(tname)
 
   debug.t_start('ezt-parse')
@@ -883,13 +882,28 @@
 
   return template
 
-def generate_page(request, view_name, data, content_type=None):
+def get_writeready_server_file(request, content_type=None):
+  """Return a file handle to a response body stream, after outputting
+  any queued special headers (on REQUEST.server) and (optionally) a
+  'Content-Type' header whose value is CONTENT_TYPE.  After this is
+  called, it is too late to add new headers to the response."""
+  if request.gzip_compress_level:
+    request.server.addheader('Content-Encoding', 'gzip')
   if content_type:
     request.server.header(content_type)
   else:
     request.server.header()
+  if request.gzip_compress_level:
+    fp = gzip.GzipFile('', 'wb', request.gzip_compress_level,
+                       request.server.file())
+  else:
+    fp = request.server.file()
+  return fp
+  
+def generate_page(request, view_name, data, content_type=None):
+  server_fp = get_writeready_server_file(request)
   template = get_view_template(request.cfg, view_name, request.language)
-  template.generate(request.server.file(), data)
+  template.generate(server_fp, data)
 
 def nav_path(request):
   """Return current path as list of items with "name" and "href" members
@@ -958,6 +972,9 @@
 def is_text(mime_type):
   return not mime_type or mime_type[:5] == 'text/'
 
+def is_cvsroot_path(roottype, path_parts):
+  return roottype == 'cvs' and path_parts and path_parts[0] == 'CVSROOT'
+
 def is_plain_text(mime_type):
   return not mime_type or mime_type == 'text/plain'
 
@@ -979,10 +996,15 @@
   """Return common hrefs and a viewability flag used for various views
   of FILENAME at revision REV whose MIME type is MIME_TYPE."""
   rev = rev and str(rev) or None
-  mime_type = mime_type or request.mime_type
+  mime_type = mime_type or guess_mime(where)
   if pathrev == -1: # cheesy default value, since we need to preserve None
     pathrev = request.pathrev
-  view_href = download_href = download_text_href = annotate_href = revision_href = None
+
+  view_href = None
+  download_href = None
+  download_text_href = None
+  annotate_href = None
+  revision_href = None
 
   if 'markup' in request.cfg.options.allowed_views:
     view_href = request.get_url(view_func=view_markup,
@@ -1034,6 +1056,25 @@
                                '(([-a-zA-Z0-9]+\.)+[A-Za-z]{2,4})')
 # XXXX Gnome hack to turn bug numbers into links
 _re_rewrite_bug = re.compile(r'((?:\b(?:bug|issue)[\s#]+|#\s*)(\d\d+))', re.I)
+
+def mangle_email_addresses(text, style=0):
+  # style=2:  truncation mangling
+  if style == 2: 
+    return re.sub(_re_rewrite_email, r'\1&#64;&hellip;', text)
+
+  # style=1:  entity-encoding and at-wrapping    
+  if style == 1: 
+    def _match_replace(matchobj):
+      return string.join(map(lambda x: '&#%d;' % (ord(x)),
+                             matchobj.group(1)), '') \
+             + ' {at} ' + \
+             string.join(map(lambda x: '&#%d;' % (ord(x)),
+                             matchobj.group(2)), '')
+    return re.sub(_re_rewrite_email, _match_replace, text)
+
+  # otherwise, no mangling
+  return text
+
 def htmlify(html, mangle_email_addrs=0):
   if not html:
     return html
@@ -1042,24 +1083,19 @@
   # XXXX Gnome hack to turn bug numbers into links
   html = re.sub(_re_rewrite_bug, r'<a href="http://bugzilla.gnome.org/show_bug.cgi?id=\2";>\1</a>', html)
   
-  if mangle_email_addrs:
-    ### FIXME: I'm sure email address mangling comes in a hundred
-    ### different flavors.  As a mechanism for defeating spam
-    ### harvesters, I suspect that merely obscuring the address only
-    ### works for a season (until the obscurity algorithm is added to
-    ### the spammers' databases of such things).  We have to actually
-    ### *lose* information here for it really matter.
-    html = re.sub(_re_rewrite_email, r'\1&#64;&hellip;', html)
-  else:
-    html = re.sub(_re_rewrite_email,
-                  r'<a href="mailto:\1&#64;\2";>\1&#64;\2</a>', html)
+  html = mangle_email_addresses(html, mangle_email_addrs)
   return html
 
-def format_log(log, cfg):
+def format_log(log, cfg, htmlize=1):
   if not log:
     return log
-  s = htmlify(log[:cfg.options.short_log_len],
-              cfg.options.mangle_email_addresses)
+  if htmlize:
+    s = htmlify(log[:cfg.options.short_log_len],
+                cfg.options.mangle_email_addresses)
+  else:
+    s = cgi.escape(log[:cfg.options.short_log_len])
+    if cfg.options.mangle_email_addresses == 2:
+      s = re.sub(_re_rewrite_email, r'\1    ', s)
   if len(log) > cfg.options.short_log_len:
     s = s + '...'
   return s
@@ -1122,7 +1158,7 @@
       s = s + ', ' + ext
   return s
 
-def common_template_data(request):
+def common_template_data(request, revision=None, mime_type=None):
   cfg = request.cfg
   data = {
     'cfg' : cfg,
@@ -1131,14 +1167,17 @@
     'docroot' : cfg.options.docroot is None                        \
                 and request.script_name + '/' + docroot_magic_path \
                 or cfg.options.docroot,
+    'username' : request.username,
     'where' : request.server.escape(request.where),
     'roottype' : request.roottype,
-    'rootname' : request.server.escape(request.rootname),
+    'rootname' : request.rootname \
+                 and request.server.escape(request.rootname) or None,
     'rootpath' : request.rootpath,
     'pathtype' : None,
     'nav_path' : nav_path(request),
     'view'     : _view_codes[request.view_func],
     'rev'      : None,
+    'lockinfo' : None,
     'view_href' : None,
     'annotate_href' : None,
     'download_href' : None,
@@ -1151,10 +1190,13 @@
     'log_href_rev': None,
     'graph_href': None,
     'rss_href' : None,
+    'roots_href' : request.get_url(view_func=view_roots, escape=1, params={}),
     'prefer_markup' : ezt.boolean(0),
   }
 
-  rev = request.query_dict.get('annotate')
+  rev = revision
+  if not rev:
+    rev = request.query_dict.get('annotate')
   if not rev:
     rev = request.query_dict.get('revision')
   if not rev and request.roottype == 'svn':
@@ -1164,33 +1206,12 @@
                   and request.repos._getrev(rev) or rev
   except vclib.InvalidRevision:
     raise debug.ViewVCException('Invalid revision', '404 Not Found')
+
   if request.pathtype == vclib.DIR:
     data['pathtype'] = 'dir'
   elif request.pathtype == vclib.FILE:
     data['pathtype'] = 'file'
 
-  data['change_root_action'], data['change_root_hidden_values'] = \
-    request.get_form(view_func=view_directory, where='', pathtype=vclib.DIR,
-                     params={'root': None})
-
-  # add in the roots for the selection
-  roots = []
-  allroots = list_roots(cfg)
-  if len(allroots):
-    rootnames = allroots.keys()
-    rootnames.sort(icmp)
-    for rootname in rootnames:
-      href = request.get_url(view_func=view_directory,
-                             where='', pathtype=vclib.DIR,
-                             params={'root': rootname}, escape=1)
-
-      ### TODO: Check root authorization.
-      roots.append(_item(name=request.server.escape(rootname),
-                         type=allroots[rootname][1],
-                         path=allroots[rootname][0],
-                         href=href))
-  data['roots'] = roots
-
   if request.path_parts:
     dir = _path_join(request.path_parts[:-1])
     data['up_href'] = request.get_url(view_func=view_directory,
@@ -1200,13 +1221,21 @@
   if request.pathtype == vclib.FILE:
     data['view_href'], data['download_href'], data['download_text_href'], \
       data['annotate_href'], data['revision_href'], data['prefer_markup'] \
-        = get_file_view_info(request, request.where,
-                             data['rev'], request.mime_type)
+        = get_file_view_info(request, request.where, data['rev'], mime_type)
     data['log_href'] = request.get_url(view_func=view_log,
                                        params={}, escape=1)
     if request.roottype == 'cvs' and cfg.options.use_cvsgraph:
       data['graph_href'] = request.get_url(view_func=view_cvsgraph,
                                            params={}, escape=1)
+    file_data = request.repos.listdir(request.path_parts[:-1],
+                                      request.pathrev, {})
+    def _only_this_file(item):
+      return item.name == request.path_parts[-1]
+    entries = filter(_only_this_file, file_data)
+    if len(entries) == 1:
+      request.repos.dirlogs(request.path_parts[:-1], request.pathrev,
+                            entries, {})
+      data['lockinfo'] = entries[0].lockinfo
   elif request.pathtype == vclib.DIR:
     data['view_href'] = request.get_url(view_func=view_directory,
                                        params={}, escape=1)
@@ -1216,22 +1245,35 @@
                                              escape=1)
     if request.roottype == 'svn':
       data['revision_href'] = request.get_url(view_func=view_revision,
-                                              params={}, escape=1)
+                                              params={'revision': data['rev']},
+                                              escape=1)
 
       data['log_href'] = request.get_url(view_func=view_log,
                                          params={}, escape=1)
 
-  if is_query_supported(request):
-    params = {}
-    if request.roottype == 'cvs' and request.pathrev:
-      params['branch'] = request.pathrev
-    data['queryform_href'] = request.get_url(view_func=view_queryform,
-                                             params=params,
-                                             escape=1)
-    data['rss_href'] = request.get_url(view_func=view_query,
-                                       params={'date': 'month',
-                                               'format': 'rss'},
-                                       escape=1)
+  if is_querydb_nonempty_for_root(request):
+    if request.pathtype == vclib.DIR:
+      params = {}
+      if request.roottype == 'cvs' and request.pathrev:
+        params['branch'] = request.pathrev
+      data['queryform_href'] = request.get_url(view_func=view_queryform,
+                                               params=params,
+                                               escape=1)
+      data['rss_href'] = request.get_url(view_func=view_query,
+                                         params={'date': 'month',
+                                                 'format': 'rss'},
+                                         escape=1)
+    elif request.pathtype == vclib.FILE:
+      parts = _path_parts(request.where)
+      where = _path_join(parts[:-1])
+      data['rss_href'] = request.get_url(view_func=view_query,
+                                         where=where,
+                                         pathtype=request.pathtype,
+                                         params={'date': 'month',
+                                                 'format': 'rss',
+                                                 'file': parts[-1],
+                                                 'file_match': 'exact'},
+                                         escape=1)
   return data
 
 def retry_read(src, reqlen=CHUNK_SIZE):
@@ -1273,185 +1315,88 @@
     if self.posttext:
       ctx.fp.write(self.posttext)
 
-class MarkupShell:
-  """A EZT callback object slamming file contents through shell tools."""
-
-  def __init__(self, cfg, fp, cmds):
-    self.fp = fp
-    self.cmds = cmds
-    self.cfg = cfg
-
-  def __call__(self, ctx):
-    ctx.fp.flush()
-    try:
-      pipe = popen.pipe_cmds(self.cmds, ctx.fp)
-      try:
-        if self.fp:
-          copy_stream(self.fp, pipe, self.cfg)
-          self.fp.close()
-          self.fp = None
-      finally:
-        pipe.close()
-    except IOError:
-      raise debug.ViewVCException \
-        ('Error running external program. Command line was: "%s"'
-         % string.join(map(lambda args: string.join(args, ' '), self.cmds),
-                       ' | '))
-
-  def __del__(self):
-    self.close()
-
-  def close(self):
-    if self.fp:
-      self.fp.close()
-      self.fp = None
-
-class MarkupEnscript(MarkupShell):
-  def __init__(self, cfg, fp, filename):
-    
-    # I've tried to pass option '-C' to enscript to generate line numbers
-    # Unfortunately this option doesn't work with HTML output in enscript
-    # version 1.6.2.
-    enscript_cmd = [cfg.utilities.enscript or 'enscript',
-                    '--color', '--language=html', '--pretty-print',
-                    '-o', '-', '-']
-
-    ### I'd like to also strip the <PRE> and </PRE> tags, too, but
-    ### can't come up with a suitable sed expression.  Using
-    ### '1,/^<PRE>$/d;/<\\/PRE>/,$d;p' gets me most of the way, but
-    ### will drop the last line of a non-newline-terminated filed.
-    sed_cmd = [cfg.utilities.sed or 'sed', '-n', '/^<PRE>$/,/<\\/PRE>$/p']
-
-    MarkupShell.__init__(self, cfg, fp, [enscript_cmd, sed_cmd])
-    self.filename = filename
-    self.cfg = cfg
-
-  def __call__(self, ctx):
-    # create a temporary file with the same name as the file in
-    # the repository so enscript can detect file type correctly
-    dir = compat.mkdtemp("", "viewvc")
-    try:
-      file = os.path.join(dir, self.filename)
-      try:
-        copy_stream(self.fp, open(file, 'wb'), self.cfg)
-        self.fp.close()
-        self.fp = None
-        self.cmds[0][-1] = file
-        MarkupShell.__call__(self, ctx)
-      finally:
-        os.unlink(file)
-    finally:
-      os.rmdir(dir)
-
-class MarkupPHP(MarkupShell):
-  def __init__(self, cfg, fp):
-    php_cmd = [cfg.utilities.php or 'php', '-q', '-s', '-n']
-    MarkupShell.__init__(self, cfg, fp, [php_cmd])
-
-class MarkupHighlight(MarkupShell):
-  def __init__(self, cfg, fp, filename):
-    highlight_cmd = [cfg.utilities.highlight or 'highlight',
-                     '--force', '--anchors', '--fragment', '--xhtml']
-
-    if cfg.options.highlight_line_numbers:
-      highlight_cmd.extend(['--linenumbers'])
-
-    if cfg.options.highlight_convert_tabs:
-      highlight_cmd.extend(['--replace-tabs',
-                            str(cfg.options.highlight_convert_tabs)])
-
-    highlight_cmd.extend(['-'])
-    MarkupShell.__init__(self, cfg, fp, [highlight_cmd])
-    self.filename = filename
-    self.cfg = cfg
-
-  def __call__(self, ctx):
-    # create a temporary file with the same name as the file in
-    # the repository so highlight can detect file type correctly
-    dir = compat.mkdtemp("", "viewvc")
+def markup_stream_pygments(request, cfg, blame_data, fp, filename, mime_type):
+  # Determine if we should use Pygments to highlight our output.
+  # Reasons not to include a) being told not to by the configuration,
+  # b) not being able to import the Pygments modules, and c) Pygments
+  # not having a lexer for our file's format.
+  blame_source = []
+  if blame_data:
+    for i in blame_data:
+      i.text = cgi.escape(i.text)
+      i.diff_href = None
+      if i.prev_rev:
+        i.diff_href = request.get_url(view_func=view_diff,
+                                      params={'r1': i.prev_rev,
+                                              'r2': i.rev},
+                                      escape=1, partial=1)
+      blame_source.append(i)
+    blame_data = blame_source
+  lexer = None
+  use_pygments = cfg.options.enable_syntax_coloration
+  try:
+    from pygments import highlight
+    from pygments.formatters import HtmlFormatter
+    from pygments.lexers import ClassNotFound, \
+                                get_lexer_by_name, \
+                                get_lexer_for_mimetype, \
+                                get_lexer_for_filename
     try:
-      file = os.path.join(dir, self.filename)
+      lexer = get_lexer_for_mimetype(mime_type)
+    except ClassNotFound:
       try:
-        copy_stream(self.fp, open(file, 'wb'), self.cfg)
-        self.fp.close()
-        self.fp = None
-        self.cmds[0][-1] = file
-        MarkupShell.__call__(self, ctx)
-      finally:
-        os.unlink(file)
-    finally:
-      os.rmdir(dir)
-
-class MarkupSourceHighlight(MarkupShell):
-  def __init__(self, cfg, fp, filename):
-    basename, ext = os.path.splitext(filename)
-    if ext:
-      ext = ext[1:]
-
-    ### Ideally, we'd use '--output xhtml-css', which would let us supply
-    ### supply our own style definitions.  Unfortunately, this appears to
-    ### be broken in source-highlight 2.3, 2.4, and 2.5 (at least).  :-(
-    highlight_cmd = [cfg.utilities.source_highlight or 'source-highlight',
-                     '--out-format', 'xhtml', '--output', 'STDOUT',
-                     '-s', ext, '--failsafe']
-    if cfg.options.source_highlight_line_numbers:
-      highlight_cmd.extend(['--line-number-ref=l_'])
-
-    sed_cmd = [cfg.utilities.sed or 'sed',
-               '-n',
-               '/^<pre><tt>/,/<\\/tt><\\/pre>$/p']
-
-    MarkupShell.__init__(self, cfg, fp, [highlight_cmd, sed_cmd])
-
-def markup_stream_python(fp, cfg):
-  if not cfg.options.use_py2html:
-    return None
-  
-  ### Convert this code to use the recipe at:
-  ###     http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52298
-  ### Note that the cookbook states all the code is licensed according to
-  ### the Python license.
-  try:
-    # See if Marc-Andre Lemburg's py2html stuff is around.
-    # http://www.egenix.com/files/python/SoftwareDescriptions.html#py2html.py
-    ### maybe restrict the import to *only* this directory?
-    sys.path.insert(0, cfg.utilities.py2html_dir)
-    import py2html
-    import PyFontify
+        lexer = get_lexer_for_filename(filename)
+      except ClassNotFound:
+        use_pygments = 0
   except ImportError:
-    return None
+    use_pygments = 0
 
-  ### It doesn't escape stuff quite right, nor does it munge URLs and
-  ### mailtos as well as we do.
-  html = cgi.escape(fp.read())
-  pp = py2html.PrettyPrint(PyFontify.fontify, "rawhtml", "color")
-  pp.set_mode_rawhtml_color()
-  html = pp.fontify(html)
-  html = re.sub(_re_rewrite_url, r'<a href="\1">\1</a>', html)
-  html = re.sub(_re_rewrite_email, r'<a href="mailto:\1";>\1</a>', html)
-  return html
-
-def markup_stream_php(fp, cfg):
-  if not cfg.options.use_php:
-    return None
-
-  # The following HACK may be be used to allow a PHP CGI executable to be
-  # invoked instead of a CLI executable, on systems that do not have PHP's
-  # CLI (command line interface) installed. Just uncomment the following lines:
-  #os.unsetenv("SERVER_SOFTWARE")
-  #os.unsetenv("SERVER_NAME")
-  #os.unsetenv("GATEWAY_INTERFACE")
-  #os.unsetenv("REQUEST_METHOD")
-  #os.unsetenv("SCRIPT_FILENAME")
-  #os.unsetenv("PATH_TRANSLATED")
-
-  return MarkupPHP(cfg, fp)
-
-markup_streamers = {
-  '.py' : markup_stream_python,
-  '.php' : markup_stream_php,
-  '.inc' : markup_stream_php,
-  }
+  # If we aren't going to be highlighting anything, just return the
+  # BLAME_SOURCE.  If there's no blame_source, we'll generate a fake
+  # one from the file contents we fetch with PATH and REV.
+  if not use_pygments:
+    if blame_source:
+      return blame_source
+    else:
+      lines = []
+      line_no = 0
+      while 1:
+        line = fp.readline()
+        if not line:
+          break
+        line_no = line_no + 1
+        item = vclib.Annotation(cgi.escape(line), line_no,
+                                None, None, None, None)
+        item.diff_href = None
+        lines.append(item)
+    return lines
+
+  # If we get here, we're highlighting something.
+  class PygmentsSink:
+    def __init__(self, blame_data):
+      if blame_data:
+        self.has_blame_data = 1
+        self.blame_data = blame_data
+      else:
+        self.has_blame_data = 0
+        self.blame_data = []
+      self.line_no = 0
+    def write(self, buf):
+      ### FIXME:  Don't bank on write() being called once per line
+      if self.has_blame_data:
+        self.blame_data[self.line_no].text = buf
+      else:
+        item = vclib.Annotation(buf, self.line_no + 1,
+                                None, None, None, None)
+        item.diff_href = None
+        self.blame_data.append(item)
+      self.line_no = self.line_no + 1
+  ps = PygmentsSink(blame_source)
+  highlight(fp.read(), lexer,
+            HtmlFormatter(nowrap=True,
+                          classprefix="pygments-",
+                          encoding='utf-8'), ps)
+  return ps.blame_data
 
 def make_time_string(date, cfg):
   """Returns formatted date string in either local time or UTC.
@@ -1477,24 +1422,88 @@
     return None
   return time.strftime("%a, %d %b %Y %H:%M:%S", time.gmtime(date)) + ' UTC'
 
-def view_markup(request):
-  if 'markup' not in request.cfg.options.allowed_views:
-    raise debug.ViewVCException('Markup view is disabled',
-                                 '403 Forbidden')
-    
+def get_itemprops(request, path_parts, rev):
+  itemprops = request.repos.itemprops(path_parts, rev)
+  propnames = itemprops.keys()
+  propnames.sort()
+  props = []
+  has_binary_props = 0
+  for name in propnames:
+    value = itemprops[name]
+    undisplayable = ezt.boolean(0)
+    # skip non-utf8 property names
+    try:
+      unicode(name, 'utf8')
+    except:
+      continue
+    # note non-utf8 property values
+    try:
+      unicode(value, 'utf8')
+    except:
+      value = None
+      undisplayable = ezt.boolean(1)
+    props.append(_item(name=name, value=value, undisplayable=undisplayable))
+  return props
+
+def calculate_mime_type(request, path_parts, rev):
+  mime_type = None
+  if not path_parts:
+    return None
+  if request.roottype == 'svn':
+    try:
+      itemprops = request.repos.itemprops(path_parts, rev)
+      mime_type = itemprops.get('svn:mime-type')
+      if mime_type:
+        return mime_type
+    except:
+      pass
+  return guess_mime(path_parts[-1])
+
+def markup_or_annotate(request, is_annotate):
   cfg = request.cfg
-  path, rev = _orig_path(request)
-  fp, revision = request.repos.openfile(path, rev)
+  path, rev = _orig_path(request, is_annotate and 'annotate' or 'revision')
+  lines = fp = image_src_href = None
+  annotation = None
+  revision = None
+  mime_type = calculate_mime_type(request, path, rev)
 
-  # Since the templates could be changed by the user, we can't provide
-  # a strong validator for this page, so we mark the etag as weak.
-  if check_freshness(request, None, revision, weak=1):
+  # Is this a viewable image type?
+  if is_viewable_image(mime_type) \
+     and 'co' in cfg.options.allowed_views:
+    fp, revision = request.repos.openfile(path, rev)
+    fp.close()
+    if check_freshness(request, None, revision, weak=1):
+      return
+    annotation = 'binary'
+    image_src_href = request.get_url(view_func=view_checkout,
+                                     params={'revision': rev}, escape=1)
+
+  # Not a viewable image.
+  else:
+    blame_source = None
+    if is_annotate:
+      # Try to annotate this file, but don't croak if we fail.
+      try:
+        blame_source, revision = request.repos.annotate(path, rev)
+        annotation = 'annotated'
+        if check_freshness(request, None, revision, weak=1):
+          return
+      except vclib.NonTextualFileContents:
+        annotation = 'binary'
+      except:
+        annotation = 'error'
+
+    fp, revision = request.repos.openfile(path, rev)
+    if check_freshness(request, None, revision, weak=1):
+      fp.close()
+      return
+    lines = markup_stream_pygments(request, cfg, blame_source, fp,
+                                   path[-1], mime_type)
     fp.close()
-    return
 
-  data = common_template_data(request)
+  data = common_template_data(request, revision)
   data.update({
-    'mime_type' : request.mime_type,
+    'mime_type' : mime_type,
     'log' : None,
     'date' : None,
     'ago' : None,
@@ -1509,11 +1518,16 @@
     'prev' : None,
     'orig_path' : None,
     'orig_href' : None,
+    'image_src_href' : image_src_href,
+    'lines' : lines,
+    'properties' : get_itemprops(request, path, rev),
+    'annotation' : annotation,
     })
 
   if cfg.options.show_log_in_markup:
-    options = {'svn_latest_log': 1}
-    revs = request.repos.itemlog(path, revision, options)
+    options = {'svn_latest_log': 1}  ### FIXME: No longer needed?
+    revs = request.repos.itemlog(path, revision, vclib.SORTBY_DEFAULT,
+                                 0, 1, options)
     entry = revs[-1]
     data.update({
         'date' : make_time_string(entry.date, cfg),
@@ -1547,50 +1561,26 @@
                                         pathtype=vclib.FILE,
                                         params={'pathrev': revision},
                                         escape=1)
-
-  markup_fp = None
-  if is_viewable_image(request.mime_type) \
-     and 'co' in cfg.options.allowed_views:
-    fp.close()
-    url = request.get_url(view_func=view_checkout, params={'revision': rev},
-                          escape=1)
-    markup_fp = '<img src="%s" alt="" /><br />' % url
-  else:
-    basename, ext = os.path.splitext(request.path_parts[-1])
-    streamer = markup_streamers.get(ext)
-    if streamer:
-      markup_fp = streamer(fp, cfg)
-
-    # If there wasn't a custom streamer, or the streamer wasn't enabled, we'll
-    # try to use one of the configured syntax highlighting programs.
-    if not markup_fp:
-      if cfg.options.use_enscript:
-        markup_fp = MarkupEnscript(cfg, fp, request.path_parts[-1])
-      elif cfg.options.use_highlight:
-        markup_fp = MarkupHighlight(cfg, fp, request.path_parts[-1])
-      elif cfg.options.use_source_highlight:
-        markup_fp = MarkupSourceHighlight(cfg, fp, request.path_parts[-1])
-      else:
-        # If no one has a suitable markup handler, we'll use the default.
-        markup_fp = MarkupPipeWrapper(cfg, fp)
     
-  data['markup'] = markup_fp
-  generate_page(request, "markup", data)
+  generate_page(request, "file", data)
+  
+def view_markup(request):
+  if 'markup' not in request.cfg.options.allowed_views:
+    raise debug.ViewVCException('Markup view is disabled',
+                                '403 Forbidden')
+  markup_or_annotate(request, 0)
+
+def view_annotate(request):
+  if 'annotate' not in request.cfg.options.allowed_views:
+    raise debug.ViewVCException('Annotation view is disabled',
+                                 '403 Forbidden')
+  markup_or_annotate(request, 1)
 
 def revcmp(rev1, rev2):
   rev1 = map(int, string.split(rev1, '.'))
   rev2 = map(int, string.split(rev2, '.'))
   return cmp(rev1, rev2)
 
-def prepare_hidden_values(params):
-  """returns variables from params encoded as a invisible HTML snippet.
-  """
-  hidden_values = []
-  for name, value in params.items():
-    hidden_values.append('<input type="hidden" name="%s" value="%s" />' %
-                         (name, value))
-  return string.join(hidden_values, '')
-
 def sort_file_data(file_data, roottype, sortdir, sortby, group_dirs):
   # convert sortdir into a sign bit
   s = sortdir == "down" and -1 or 1
@@ -1643,7 +1633,28 @@
   return cmp(string.lower(x), string.lower(y))
 
 def view_roots(request):
+  if 'roots' not in request.cfg.options.allowed_views:
+    raise debug.ViewVCException('Root listing view is disabled',
+                                '403 Forbidden')
+  
+  # add in the roots for the selection
+  roots = []
+  expand_root_parents(request.cfg)
+  allroots = list_roots(request)
+  if len(allroots):
+    rootnames = allroots.keys()
+    rootnames.sort(icmp)
+    for rootname in rootnames:
+      href = request.get_url(view_func=view_directory,
+                             where='', pathtype=vclib.DIR,
+                             params={'root': rootname}, escape=1)
+      roots.append(_item(name=request.server.escape(rootname),
+                         type=allroots[rootname][1],
+                         path=allroots[rootname][0],
+                         href=href))
+
   data = common_template_data(request)
+  data['roots'] = roots
   generate_page(request, "roots", data)
 
 def view_roots_txt(request):
@@ -1659,7 +1670,7 @@
       rev = request.repos._getrev(request.pathrev)
     except vclib.InvalidRevision:
       raise debug.ViewVCException('Invalid revision', '404 Not Found')
-    tree_rev = vclib.svn.created_rev(request.repos, request.where, rev)
+    tree_rev = request.repos.created_rev(request.where, rev)
     if check_freshness(request, None, str(tree_rev), weak=1):
       return
 
@@ -1671,25 +1682,9 @@
                                            cfg.options.hide_attic))
     options["cvs_subdirs"] = (cfg.options.show_subdir_lastmod and
                               cfg.options.show_logs)
-
   file_data = request.repos.listdir(request.path_parts, request.pathrev,
                                     options)
 
-  # Filter file list if a regex is specified
-  search_re = request.query_dict.get('search', '')
-  if cfg.options.use_re_search and search_re:
-    file_data = search_files(request.repos, request.path_parts,
-                             request.pathrev, file_data, search_re)
-
-  # Further filter the file list based on what the user is permitted
-  # to see.  We do this before called dirlogs because it'll save us
-  # some work there, but also so it won't harvest tag/branch names
-  # from unauthorized files.
-  def _auth_filter(item):
-    return request.auth.check_path_access(request.path_parts \
-                                          + [item.name])
-  file_data = filter(_auth_filter, file_data)
-
   # sort with directories first, and using the "sortby" criteria
   sortby = request.query_dict.get('sortby', cfg.options.sort_by) or 'file'
   sortdir = request.query_dict.get('sortdir', 'up')
@@ -1711,6 +1706,8 @@
       file.log = None
       file.author = None
       file.size = None
+      file.lockinfo = None
+      file.dead = None
     sort_file_data(file_data, request.roottype, sortdir, sortby,
                    cfg.options.sort_group_dirs)
     # request dirlogs only for the slice of files in "this page"
@@ -1723,6 +1720,12 @@
                    cfg.options.sort_group_dirs)
   debug.t_end("dirlogs")
 
+  # If a regex is specified, build a compiled form thereof for filtering
+  searchstr = None
+  search_re = request.query_dict.get('search', '')
+  if cfg.options.use_re_search and search_re:
+    searchstr = re.compile(search_re)
+
   # loop through entries creating rows and changing these values
   rows = [ ]
   num_displayed = 0
@@ -1738,7 +1741,10 @@
                 date=None, ago=None, view_href=None, log_href=None,
                 revision_href=None, annotate_href=None, download_href=None,
                 download_text_href=None, prefer_markup=ezt.boolean(0))
-
+    if request.roottype == 'cvs' and file.absent:
+      continue
+    if cfg.options.hide_errorful_entries and file.errors:
+      continue
     row.rev = file.rev
     row.author = file.author
     row.state = (request.roottype == 'cvs' and file.dead) and 'dead' or ''
@@ -1748,7 +1754,7 @@
     if cfg.options.show_logs:
       row.short_log = format_log(file.log, cfg)
       row.log = htmlify(file.log, cfg.options.mangle_email_addresses)
-
+    row.lockinfo = file.lockinfo
     row.anchor = request.server.escape(file.name)
     row.name = request.server.escape(file.name)
     row.pathtype = (file.kind == vclib.FILE and 'file') or \
@@ -1756,9 +1762,9 @@
     row.errors = file.errors
 
     if file.kind == vclib.DIR:
-
-      if (request.roottype == 'cvs' and cfg.options.hide_cvsroot
-          and where == '' and file.name == 'CVSROOT'):
+      if cfg.options.hide_cvsroot \
+         and is_cvsroot_path(request.roottype,
+                             request.path_parts + [file.name]):
         continue
     
       row.view_href = request.get_url(view_func=view_directory,
@@ -1786,19 +1792,25 @@
                                        escape=1)
       
     elif file.kind == vclib.FILE:
-      
+      if searchstr is not None:
+        if request.roottype == 'cvs' and (file.errors or file.dead):
+          continue
+        if not search_file(request.repos, request.path_parts + [file.name],
+                           request.pathrev, searchstr):
+          continue
       if request.roottype == 'cvs' and file.dead:
         num_dead = num_dead + 1
         if hideattic:
           continue
+        
       num_displayed = num_displayed + 1
 
       file_where = where_prefix + file.name
       if request.roottype == 'svn': 
         row.size = file.size
 
-      ### for Subversion, we should first try to get this from the properties
-      row.mime_type = guess_mime(file.name)
+      row.mime_type = calculate_mime_type(request, _path_parts(file_where),
+                                          file.rev)
       row.view_href, row.download_href, row.download_text_href, \
                      row.annotate_href, row.revision_href, \
                      row.prefer_markup \
@@ -1852,6 +1864,7 @@
     'hide_attic_href' : None,
     'branch_tags': None,
     'plain_tags': None,
+    'properties': get_itemprops(request, request.path_parts, request.pathrev),
   })
 
   # clicking on sort column reverses sort order
@@ -1888,7 +1901,7 @@
     data['tree_rev_href'] = request.get_url(view_func=view_revision,
                                             params={'revision': tree_rev},
                                             escape=1)
-    data['youngest_rev'] = vclib.svn.get_youngest_revision(request.repos)
+    data['youngest_rev'] = request.repos.get_youngest_revision()
     data['youngest_rev_href'] = request.get_url(view_func=view_revision,
                                                 params={},
                                                 escape=1)
@@ -1900,8 +1913,7 @@
   pathrev_form(request, data)
 
   ### one day, if EZT has "or" capability, we can lose this
-  data['search_re_form'] = ezt.boolean(cfg.options.use_re_search
-                                       and (num_displayed > 0 or search_re))
+  data['search_re_form'] = ezt.boolean(cfg.options.use_re_search)
   if data['search_re_form']:
     data['search_re_action'], data['search_re_hidden_values'] = \
       request.get_form(params={'search': None})
@@ -1918,7 +1930,7 @@
   # Create the picklist
   picklist = data['picklist'] = []
   for i in range(0, len(data[key]), pagesize):
-    pick = _item(start=None, end=None, count=None)
+    pick = _item(start=None, end=None, count=None, more=ezt.boolean(0))
     pick.start = getattr(data[key][i], local_name)
     pick.count = i
     pick.page = (i / pagesize) + 1
@@ -1941,6 +1953,38 @@
   # Slice
   return data[key][pagestart:pageend]
 
+def paging_sws(data, key, pagestart, local_name, pagesize, offset):
+  """Implement sliding window-style paging."""
+  # Create the picklist
+  last_requested = pagestart + (EXTRA_PAGES * pagesize)
+  picklist = data['picklist'] = []
+  has_more = ezt.boolean(0)
+  for i in range(0, len(data[key]), pagesize):
+    pick = _item(start=None, end=None, count=None, more=ezt.boolean(0))
+    pick.start = getattr(data[key][i], local_name)
+    pick.count = offset + i
+    pick.page = (pick.count / pagesize) + 1
+    try:
+      pick.end = getattr(data[key][i+pagesize-1], local_name)
+    except IndexError:
+      pick.end = getattr(data[key][-1], local_name)   
+    picklist.append(pick)
+    if pick.count >= last_requested:
+      pick.more = ezt.boolean(1)
+      break
+  data['picklist_len'] = len(picklist)
+  first = pagestart - offset
+  # FIXME: first can be greater than the length of data[key] if
+  # you select a tag or search while on a page other than the first.
+  # Should reset to the first page, but this test won't do that every
+  # time that it is needed.  Problem might go away if we don't hide
+  # non-matching files when selecting for tags or searching.
+  if first > len(data[key]):
+    pagestart = 0
+  pageend = first + pagesize
+  # Slice
+  return data[key][first:pageend]
+
 def pathrev_form(request, data):
   lastrev = None
 
@@ -1954,12 +1998,12 @@
                                'orig_view': _view_codes.get(request.view_func)})
 
     if request.pathrev:
-      youngest = vclib.svn.get_youngest_revision(request.repos)
-      lastrev = vclib.svn.last_rev(request.repos, request.where,
-                                   request.pathrev, youngest)[0]
+      youngest = request.repos.get_youngest_revision()
+      lastrev = request.repos.last_rev(request.where, request.pathrev,
+                                       youngest)[0]
 
       if lastrev == youngest:
-         lastrev = None
+        lastrev = None
 
   data['pathrev'] = request.pathrev
   data['lastrev'] = lastrev
@@ -1981,7 +2025,7 @@
   pathrev = request.query_dict.get('orig_pathrev') 
   view = _views.get(request.query_dict.get('orig_view'))
   
-  youngest = vclib.svn.get_youngest_revision(request.repos)
+  youngest = request.repos.get_youngest_revision()
 
   # go out of the way to allow revision numbers higher than youngest
   try:
@@ -1997,8 +2041,7 @@
   if _repos_pathtype(request.repos, _path_parts(path), new_pathrev):
     pathrev = new_pathrev
   else:
-    pathrev, path = vclib.svn.last_rev(request.repos, path, pathrev, 
-                                       new_pathrev)
+    pathrev, path = request.repos.last_rev(path, pathrev, new_pathrev)
     # allow clearing sticky revision by submitting empty string
     if new_pathrev is None and pathrev == youngest:
       pathrev = None
@@ -2008,37 +2051,43 @@
                                           pathtype=pathtype,
                                           params={'pathrev': pathrev}))
 
-def logsort_date_cmp(rev1, rev2):
-  # sort on date; secondary on revision number
-  return -cmp(rev1.date, rev2.date) or -cmp(rev1.number, rev2.number)
-
-def logsort_rev_cmp(rev1, rev2):
-  # sort highest revision first
-  return -cmp(rev1.number, rev2.number)
-
 def view_log(request):
   cfg = request.cfg
   diff_format = request.query_dict.get('diff_format', cfg.options.diff_format)
-  logsort = request.query_dict.get('logsort', cfg.options.log_sort)
   pathtype = request.pathtype
 
-  if pathtype is vclib.DIR and request.roottype == 'cvs':
-    raise debug.ViewVCException('Unsupported feature: log view on CVS '
-                                 'directory', '400 Bad Request')
+  if pathtype is vclib.DIR:
+    if request.roottype == 'cvs':
+      raise debug.ViewVCException('Unsupported feature: log view on CVS '
+                                  'directory', '400 Bad Request')
+    mime_type = None
+  else:
+    mime_type = calculate_mime_type(request, request.path_parts, request.pathrev)
 
   options = {}
   options['svn_show_all_dir_logs'] = 1 ### someday make this optional?
   options['svn_cross_copies'] = cfg.options.cross_copies
-    
+
+  logsort = request.query_dict.get('logsort', cfg.options.log_sort)
+  if request.roottype == "svn":
+    sortby = vclib.SORTBY_DEFAULT
+    logsort = None
+  else:
+    if logsort == 'date':
+      sortby = vclib.SORTBY_DATE
+    elif logsort == 'rev':
+      sortby = vclib.SORTBY_REV
+    else:
+      sortby = vclib.SORTBY_DEFAULT
+
+  first = last = 0
+  if cfg.options.use_pagesize:
+    log_pagestart = int(request.query_dict.get('log_pagestart', 0))
+    first = log_pagestart - min(log_pagestart,
+                                (EXTRA_PAGES * cfg.options.use_pagesize))
+    last = log_pagestart + ((EXTRA_PAGES + 1) * cfg.options.use_pagesize) + 1
   show_revs = request.repos.itemlog(request.path_parts, request.pathrev,
-                                    options)
-  if logsort == 'date':
-    show_revs.sort(logsort_date_cmp)
-  elif logsort == 'rev':
-    show_revs.sort(logsort_rev_cmp)
-  else:
-    # no sorting
-    pass
+                                    sortby, first, last - first, options)
 
   # selected revision
   selected_rev = request.query_dict.get('r1')
@@ -2058,6 +2107,7 @@
       entry.ago = html_time(request, rev.date, 1)
     entry.log = htmlify(rev.log or "", cfg.options.mangle_email_addresses)
     entry.size = rev.size
+    entry.lockinfo = rev.lockinfo
     entry.branch_point = None
     entry.next_main = None
     entry.orig_path = None
@@ -2133,8 +2183,7 @@
     if pathtype is vclib.FILE:
       entry.view_href, entry.download_href, entry.download_text_href, \
         entry.annotate_href, entry.revision_href, entry.prefer_markup \
-        = get_file_view_info(request, request.where, rev.string,
-                             request.mime_type)
+        = get_file_view_info(request, request.where, rev.string, mime_type)
     else:
       entry.revision_href = request.get_url(view_func=view_revision,
                                             params={'revision': rev.string},
@@ -2148,7 +2197,9 @@
     # calculate diff links
     if selected_rev != entry.rev:
       entry.sel_for_diff_href = \
-        request.get_url(view_func=view_log, params={'r1': entry.rev}, escape=1)
+        request.get_url(view_func=view_log,
+                        params={'r1': entry.rev},
+                        escape=1)
     if entry.prev is not None:
       entry.diff_to_prev_href = \
         request.get_url(view_func=view_diff,
@@ -2193,18 +2244,18 @@
   data = common_template_data(request)
   data.update({
     'default_branch' : None,
-    'mime_type' : request.mime_type,
+    'mime_type' : mime_type,
     'rev_selected' : selected_rev,
     'diff_format' : diff_format,
     'logsort' : logsort,
     'human_readable' : ezt.boolean(diff_format in ('h', 'l')),
     'log_pagestart' : None,
     'entries': entries,
-    'prefer_markup' : ezt.boolean(0),
-    'view_href' : None,
-    'download_href': None,
-    'download_text_href': None,
-    'annotate_href': None,
+    'head_prefer_markup' : ezt.boolean(0),
+    'head_view_href' : None,
+    'head_download_href': None,
+    'head_download_text_href': None,
+    'head_annotate_href': None,
     'tag_prefer_markup' : ezt.boolean(0),
     'tag_view_href' : None,
     'tag_download_href': None,
@@ -2214,10 +2265,6 @@
 
   lastrev = pathrev_form(request, data)
 
-  if cfg.options.use_pagesize:
-    data['log_paging_action'], data['log_paging_hidden_values'] = \
-      request.get_form(params={'log_pagestart': None})
-  
   data['diff_select_action'], data['diff_select_hidden_values'] = \
     request.get_form(view_func=view_diff,
                      params={'r1': None, 'r2': None, 'tr1': None,
@@ -2230,20 +2277,19 @@
     if not request.pathrev or lastrev is None:
       view_href, download_href, download_text_href, \
         annotate_href, revision_href, prefer_markup \
-        = get_file_view_info(request, request.where, None,
-                             request.mime_type, None)
+        = get_file_view_info(request, request.where, None, mime_type, None)
       data.update({
-        'view_href': view_href,
-        'download_href': download_href,
-        'download_text_href': download_text_href,
-        'annotate_href': annotate_href,
-        'prefer_markup': prefer_markup,
+        'head_view_href': view_href,
+        'head_download_href': download_href,
+        'head_download_text_href': download_text_href,
+        'head_annotate_href': annotate_href,
+        'head_prefer_markup': prefer_markup,
         })
 
     if request.pathrev and request.roottype == 'cvs':
       view_href, download_href, download_text_href, \
         annotate_href, revision_href, prefer_markup \
-        = get_file_view_info(request, request.where, None, request.mime_type)
+        = get_file_view_info(request, request.where, None, mime_type)
       data.update({
         'tag_view_href': view_href,
         'tag_download_href': download_href,
@@ -2252,8 +2298,8 @@
         'tag_prefer_markup': prefer_markup,
         })
   else:
-    data['view_href'] = request.get_url(view_func=view_directory, 
-                                        params={}, escape=1)
+    data['head_view_href'] = request.get_url(view_func=view_directory, 
+                                             params={}, escape=1)
 
   taginfo = options.get('cvs_tags', {})
   tagitems = taginfo.items()
@@ -2282,9 +2328,11 @@
       plain_tags.append(tag)
 
   if cfg.options.use_pagesize:
+    data['log_paging_action'], data['log_paging_hidden_values'] = \
+      request.get_form(params={'log_pagestart': None})
     data['log_pagestart'] = int(request.query_dict.get('log_pagestart',0))
-    data['entries'] = paging(data, 'entries', data['log_pagestart'],
-                             'rev', cfg.options.use_pagesize)
+    data['entries'] = paging_sws(data, 'entries', data['log_pagestart'],
+                                 'rev', cfg.options.use_pagesize, first)
 
   generate_page(request, "log", data)
 
@@ -2301,96 +2349,13 @@
 
   # The revision number acts as a strong validator.
   if not check_freshness(request, None, revision):
-    request.server.header(request.query_dict.get('content-type')
-                          or request.mime_type or 'text/plain')
-    copy_stream(fp, request.server.file(), cfg)
+    mime_type = request.query_dict.get('content-type') \
+                or calculate_mime_type(request, path, rev) \
+                or 'text/plain'
+    server_fp = get_writeready_server_file(request, mime_type)
+    copy_stream(fp, server_fp, cfg)
   fp.close()
 
-def view_annotate(request):
-  if 'annotate' not in request.cfg.options.allowed_views:
-    raise debug.ViewVCException('Annotation view is disabled',
-                                 '403 Forbidden')
-
-  cfg = request.cfg
-  path, rev = _orig_path(request, 'annotate')
-
-  ### be nice to hook this into the template...
-  import blame
-
-  diff_url = request.get_url(view_func=view_diff,
-                             params={'r1': None, 'r2': None},
-                             escape=1, partial=1)
-
-  include_url = request.get_url(view_func=view_log, where='/WHERE/',
-                                pathtype=vclib.FILE, params={}, escape=1)
-
-  try:
-    source, revision = blame.blame(request.repos, path,
-                                   diff_url, include_url, rev)
-  except vclib.NonTextualFileContents:
-    raise debug.ViewVCException('Unable to perform line-based annotation on '
-                                'non-textual file contents',
-                                '400 Bad Request')
-
-  data = common_template_data(request)
-  data.update({
-    'mime_type' : request.mime_type,
-    'log' : None,
-    'date' : None,
-    'ago' : None,
-    'author' : None,
-    'branches' : None,
-    'tags' : None,
-    'branch_points' : None,
-    'changed' : None,
-    'size' : None,
-    'state' : None,
-    'vendor_branch' : None,
-    'prev' : None,
-    'lines': source,
-    'orig_path': None,
-    'orig_href': None,
-    })
-
-  if cfg.options.show_log_in_markup:
-    options = {'svn_latest_log': 1}
-    revs = request.repos.itemlog(path, revision, options)
-    entry = revs[-1]
-    data.update({
-        'date' : make_time_string(entry.date, cfg),
-        'author' : entry.author,
-        'changed' : entry.changed,
-        'log' : htmlify(entry.log, cfg.options.mangle_email_addresses),
-        'size' : entry.size,
-        })
-
-    if entry.date is not None:
-      data['ago'] = html_time(request, entry.date, 1)
-      
-    if request.roottype == 'cvs':
-      branch = entry.branch_number
-      prev = entry.prev or entry.parent
-      data.update({
-        'state' : entry.dead and 'dead',
-        'prev' : prev and prev.string,
-        'vendor_branch' : ezt.boolean(branch and branch[2] % 2 == 1),
-        'branches' : string.join(map(lambda x: x.name, entry.branches), ', '),
-        'tags' : string.join(map(lambda x: x.name, entry.tags), ', '),
-        'branch_points': string.join(map(lambda x: x.name,
-                                         entry.branch_points), ', ')
-        })
-
-  if path != request.path_parts:
-    orig_path = _path_join(path)
-    data['orig_path'] = orig_path
-    data['orig_href'] = request.get_url(view_func=view_log,
-                                        where=orig_path,
-                                        pathtype=vclib.FILE,
-                                        params={'pathrev': revision},
-                                        escape=1)
-
-  generate_page(request, "annotate", data)
-
 def view_cvsgraph_image(request):
   "output the image rendered by cvsgraph"
   # this function is derived from cgi/cvsgraphmkimg.cgi
@@ -2403,14 +2368,14 @@
   # If cvsgraph can't find its supporting libraries, uncomment and set
   # accordingly.  Do the same in view_cvsgraph().
   #os.environ['LD_LIBRARY_PATH'] = '/usr/lib:/usr/local/lib:/path/to/cvsgraph'
-  
-  request.server.header('image/png')
+
   rcsfile = request.repos.rcsfile(request.path_parts)
   fp = popen.popen(cfg.utilities.cvsgraph or 'cvsgraph',
                    ("-c", cfg.path(cfg.options.cvsgraph_conf),
                     "-r", request.repos.rootpath,
                     rcsfile), 'rb', 0)
-  copy_stream(fp, request.server.file(), cfg)
+  
+  copy_stream(fp, get_writeready_server_file(request, 'image/png'), cfg)
   fp.close()
 
 def view_cvsgraph(request):
@@ -2428,8 +2393,8 @@
   #os.environ['LD_LIBRARY_PATH'] = '/usr/lib:/usr/local/lib:/path/to/cvsgraph'
 
   imagesrc = request.get_url(view_func=view_cvsgraph_image, escape=1)
-
-  view = default_view(request.mime_type, cfg)
+  mime_type = guess_mime(request.where)
+  view = default_view(mime_type, cfg)
   up_where = _path_join(request.path_parts[:-1])
 
   # Create an image map
@@ -2461,58 +2426,23 @@
 
   generate_page(request, "graph", data)
 
-def search_files(repos, path_parts, rev, files, search_re):
-  """ Search files in a directory for a regular expression.
-
-  Does a check-out of each file in the directory.  Only checks for
-  the first match.  
-  """
-
-  # Pass in search regular expression. We check out
-  # each file and look for the regular expression. We then return the data
-  # for all files that match the regex.
-
-  # Compile to make sure we do this as fast as possible.
-  searchstr = re.compile(search_re)
-
-  # Will become list of files that have at least one match.
-  # new_file_list also includes directories.
-  new_file_list = [ ]
-
-  # Loop on every file (and directory)
-  for file in files:
-    # Is this a directory?  If so, append name to new_file_list
-    # and move to next file.
-    if file.kind != vclib.FILE:
-      new_file_list.append(file)
-      continue
-
-    # Only files at this point
-    
-    # Shouldn't search binary files, or should we?
-    # Should allow all text mime types to pass.
-    if not is_text(guess_mime(file.name)):
-      continue
-
-    # Only text files at this point
-
-    # Assign contents of checked out file to fp.
-    fp = repos.openfile(path_parts + [file.name], rev)[0]
-
-    # Read in each line, use re.search to search line.
-    # If successful, add file to new_file_list and break.
-    while 1:
-      line = fp.readline()
-      if not line:
-        break
-      if searchstr.search(line):
-        new_file_list.append(file)
-        # close down the pipe (and wait for the child to terminate)
-        fp.close()
-        break
-
-  return new_file_list
-
+def search_file(repos, path_parts, rev, search_re):
+  """Return 1 iff the contents of the file at PATH_PARTS in REPOS as
+  of revision REV matches regular expression SEARCH_RE."""
+
+  # Read in each line of a checked-out file, and then use re.search to
+  # search line.
+  fp = repos.openfile(path_parts, rev)[0]
+  matches = 0
+  while 1:
+    line = fp.readline()
+    if not line:
+      break
+    if search_re.search(line):
+      matches = 1
+      fp.close()
+      break
+  return matches
 
 def view_doc(request):
   """Serve ViewVC static content locally.
@@ -2546,16 +2476,16 @@
 
   request.server.addheader('Content-Length', content_length)
   if document[-3:] == 'png':
-    request.server.header('image/png')
+    mime_type = 'image/png'
   elif document[-3:] == 'jpg':
-    request.server.header('image/jpeg')
+    mime_type = 'image/jpeg'
   elif document[-3:] == 'gif':
-    request.server.header('image/gif')
+    mime_type = 'image/gif'
   elif document[-3:] == 'css':
-    request.server.header('text/css')
+    mime_type = 'text/css'
   else: # assume HTML:
-    request.server.header()
-  copy_stream(fp, request.server.file(), cfg)
+    mime_type = None
+  copy_stream(fp, get_writeready_server_file(request, mime_type), cfg)
   fp.close()
 
 def rcsdiff_date_reformat(date_str, cfg):
@@ -2791,33 +2721,21 @@
 
 
 def _get_diff_path_parts(request, query_key, rev, base_rev):
+  repos = request.repos
   if request.query_dict.has_key(query_key):
-    path = request.query_dict[query_key]
-    parts = _path_parts(path)
-    if not request.auth.check_path_access(parts):
-      raise debug.ViewVCNotAuthorizedException(self.username,
-                                               'file "%s"' % (path))
+    parts = _path_parts(request.query_dict[query_key])
   elif request.roottype == 'svn':
     try:
-      repos = request.repos
-      path = vclib.svn.get_location(repos, request.where,
-                                    repos._getrev(base_rev),
-                                    repos._getrev(rev))
-      parts = _path_parts(path)      
-      if not request.auth.check_path_access(parts):
-        raise debug.ViewVCNotAuthorizedException(self.username,
-                                                 'file "%s"' % (path))
+      parts = _path_parts(repos.get_location(request.where,
+                                             repos._getrev(base_rev),
+                                             repos._getrev(rev)))
     except vclib.InvalidRevision:
       raise debug.ViewVCException('Invalid path(s) or revision(s) passed '
                                    'to diff', '400 Bad Request')
     except vclib.ItemNotFound:
       raise debug.ViewVCException('Invalid path(s) or revision(s) passed '
                                    'to diff', '400 Bad Request')
-    if not request.auth.check_path_access(parts):
-      raise debug.ViewVCException('Invalid path(s) or revision(s) passed '
-                                   'to diff', '400 Bad Request')
   else:
-    # NOTE: authorization already checked in run_viewvc()
     parts = request.path_parts
   return parts
 
@@ -2906,9 +2824,9 @@
   date1, date2, flag, headers = diff_parse_headers(fp, diff_type, rev1, rev2,
                                                    sym1, sym2)
 
-  request.server.header('text/plain')
-  request.server.file().write(headers)
-  copy_stream(fp, request.server.file(), cfg)
+  server_fp = get_writeready_server_file(request, 'text/plain')
+  server_fp.write(headers)
+  copy_stream(fp, server_fp, cfg)
   fp.close()
 
 
@@ -2983,7 +2901,8 @@
   path_left = _path_join(p1)
   path_right = _path_join(p2)
   if fp:
-    date1, date2, flag, headers = diff_parse_headers(fp, diff_type, rev1, rev2,
+    date1, date2, flag, headers = diff_parse_headers(fp, diff_type,
+                                                     rev1, rev2,
                                                      sym1, sym2)
   else:
     date1 = date2 = flag = headers = None
@@ -3058,7 +2977,8 @@
 
   # generate a GNU tar extension header for long names.
   if len(name) >= 100:
-    generate_tarball_header(out, '././@LongLink', len(name), 0644, 0, 0, 0, 'L')
+    generate_tarball_header(out, '././@LongLink', len(name),
+                            0644, 0, 0, 0, 'L')
     out.write(name)
     out.write('\0' * (511 - ((len(name) + 511) % 512)))
 
@@ -3121,7 +3041,7 @@
     for file in entries:
       if cvs and (file.kind != vclib.FILE or file.rev is None or file.dead):
         continue
-      if file.date > dir_mtime:
+      if (file.date is not None) and (file.date > dir_mtime):
         dir_mtime = file.date
 
   # Push current directory onto the stack.
@@ -3140,8 +3060,6 @@
       continue
     if cvs and (file.rev is None or file.dead):
       continue
-    if not request.auth.check_path_access(rep_path + [file.name]):
-      continue
 
     # If we get here, we've seen at least one valid file in the
     # current directory.  For CVS, we need to make sure there are
@@ -3151,9 +3069,11 @@
         generate_tarball_header(out, dir, mtime=dir_mtime)
       del stack[:]
 
-    if cvs:
-      info = os.stat(file.path)
-      mode = (info[stat.ST_MODE] & 0555) | 0200
+    # Calculate the mode for the file.  Sure, we could look directly
+    # at the ,v file in CVS, but that's a layering violation we'd like
+    # to avoid as much as possible.
+    if request.repos.isexecutable(rep_path + [file.name], request.pathrev):
+      mode = 0755
     else:
       mode = 0644
 
@@ -3164,7 +3084,8 @@
     fp.close()
 
     generate_tarball_header(out, tar_dir + file.name,
-                            len(contents), mode, file.date)
+                            len(contents), mode,
+                            file.date is not None and file.date or 0)
     out.write(contents)
     out.write('\0' * (511 - ((len(contents) + 511) % 512)))
 
@@ -3173,12 +3094,8 @@
   for file in entries:
     if file.errors or file.kind != vclib.DIR:
       continue
-    if (not rep_path) \
-       and cvs \
-       and request.cfg.options.hide_cvsroot \
-       and file.name == 'CVSROOT':
-        continue
-    if not request.auth.check_path_access(rep_path + [file.name]):
+    if request.cfg.options.hide_cvsroot \
+       and is_cvsroot_path(request.roottype, rep_path + [file.name]):
       continue
 
     mtime = request.roottype == 'svn' and file.date or None
@@ -3196,11 +3113,19 @@
 
   if debug.TARFILE_PATH:
     fp = open(debug.TARFILE_PATH, 'w')
-  else:
-    request.server.header('application/octet-stream')
+  else:    
+    tarfile = request.rootname
+    if request.path_parts:
+      tarfile = "%s-%s" % (tarfile, request.path_parts[-1])
+    request.server.addheader('Content-Disposition',
+                             'attachment; filename="%s.tar.gz"' % (tarfile))
+    server_fp = get_writeready_server_file(request, 'application/x-gzip')
     request.server.flush()
-    fp = popen.pipe_cmds([(cfg.utilities.gzip or 'gzip', '-c', '-n')])
-  
+    
+    # Try to use the Python gzip module, if available; otherwise,
+    # we'll use the configured 'gzip' binary.
+    fp = gzip.GzipFile('', 'wb', 9, server_fp)
+
   ### FIXME: For Subversion repositories, we can get the real mtime of the
   ### top-level directory here.
   generate_tarball(fp, request, [], [])
@@ -3230,20 +3155,17 @@
     rev = request.repos._getrev(query_dict.get('revision'))
   except vclib.InvalidRevision:
     raise debug.ViewVCException('Invalid revision', '404 Not Found')
-    
-  # The revision number acts as a weak validator.
-  if check_freshness(request, None, str(rev), weak=1):
+  youngest_rev = request.repos.get_youngest_revision()
+  
+  # The revision number acts as a weak validator (but we tell browsers
+  # not to cache the youngest revision).
+  if rev != youngest_rev and check_freshness(request, None, str(rev), weak=1):
     return
 
   # Fetch the revision information.
   date, author, msg, changes = request.repos.revinfo(rev)
   date_str = make_time_string(date, cfg)
 
-  # Filter the changes list based on what the user is permitted to see.
-  def _auth_filter(item):
-    return request.auth.check_path_access(item.path_parts)
-  changes = filter(_auth_filter, changes)
-
   # Sort the changes list by path.
   def changes_sort_by_path(a, b):
     return cmp(a.path_parts, b.path_parts)
@@ -3272,13 +3194,6 @@
   for change in changes:
     change.view_href = change.diff_href = change.type = change.log_href = None
 
-    # If this path was copied, and the user is permitted to see this
-    # path but not the path from which it was copied, then lie about
-    # this *not* being a copy.
-    if change.copied \
-       and not request.auth.check_path_access(change.base_path_parts):
-      change.copied = 0
-
     # If the path is newly added, don't claim text or property
     # modifications.
     if (change.action == vclib.ADDED or change.action == vclib.REPLACED) \
@@ -3286,7 +3201,7 @@
       change.text_changed = 0
       change.props_changed = 0
 
-    # Calculate the various view link URLs (for which we must have a pathtype).
+    # Calculate the view link URLs (for which we must have a pathtype).
     if change.pathtype:
       view_func = None
       if change.pathtype is vclib.FILE \
@@ -3350,7 +3265,7 @@
                                     pathtype=None,
                                     params={'revision': str(rev - 1)},
                                     escape=1)
-  if rev < request.repos.youngest:
+  if rev < request.repos.get_youngest_revision():
     next_rev_href = request.get_url(view_func=view_revision,
                                     where=None,
                                     pathtype=None,
@@ -3378,6 +3293,8 @@
   data['jump_rev_action'], data['jump_rev_hidden_values'] = \
     request.get_form(params={'revision': None})
 
+  if rev == youngest_rev:
+    request.server.addheader("Cache-control", "no-store")
   generate_page(request, "revision", data)
 
 def is_query_supported(request):
@@ -3386,6 +3303,22 @@
          and request.pathtype == vclib.DIR \
          and request.roottype in ['cvs', 'svn']
 
+def is_querydb_nonempty_for_root(request):
+  """Return 1 iff commits database integration is supported *and* the
+  current root is found in that database.  Only does this check if
+  check_database is set to 1."""
+  if request.cfg.cvsdb.enabled and request.roottype in ['cvs', 'svn']:
+    if request.cfg.cvsdb.check_database_for_root:
+      global cvsdb
+      import cvsdb
+      db = cvsdb.ConnectDatabaseReadOnly(request.cfg)
+      repos_root, repos_dir = cvsdb.FindRepository(db, request.rootpath)
+      if repos_root:
+        return 1
+    else:
+      return 1
+  return 0
+
 def view_queryform(request):
   if not is_query_supported(request):
     raise debug.ViewVCException('Can not query project root "%s" at "%s".'
@@ -3420,9 +3353,11 @@
 
   generate_page(request, "query_form", data)
 
-def parse_date(s):
-  '''Parse a date string from the query form.'''
-  match = re.match(r'^(\d\d\d\d)-(\d\d)-(\d\d)(?:\ +(\d\d):(\d\d)(?::(\d\d))?)?$', s)
+def parse_date(datestr):
+  """Parse a date string from the query form."""
+  
+  match = re.match(r'^(\d\d\d\d)-(\d\d)-(\d\d)(?:\ +'
+                   '(\d\d):(\d\d)(?::(\d\d))?)?$', datestr)
   if match:
     year = int(match.group(1))
     month = int(match.group(2))
@@ -3505,7 +3440,7 @@
   return string.join(ret, '')
 
 def prev_rev(rev):
-  '''Returns a string representing the previous revision of the argument.'''
+  """Returns a string representing the previous revision of the argument."""
   r = string.split(rev, '.')
   # decrement final revision component
   r[-1] = str(int(r[-1]) - 1)
@@ -3514,67 +3449,94 @@
     r = r[:-2]
   return string.join(r, '.')
 
-def build_commit(request, files, limited_files, dir_strip):
+def build_commit(request, files, max_files, dir_strip, format):
+  """Return a commit object build from the information in FILES, or
+  None if no allowed files are present in the set.  DIR_STRIP is the
+  path prefix to remove from the commit object's set of files.  If
+  MAX_FILES is non-zero, it is used to limit the number of files
+  returned in the commit object.  FORMAT is the requested output
+  format of the query request."""
+
   cfg = request.cfg
-  commit = _item(num_files=len(files), files=[])
-  commit.limited_files = ezt.boolean(limited_files)
+  author = files[0].GetAuthor()
+  date = files[0].GetTime()
   desc = files[0].GetDescription()
-  commit.log = htmlify(desc, cfg.options.mangle_email_addresses)
-  commit.short_log = format_log(desc, cfg)
-  commit.author = request.server.escape(files[0].GetAuthor())
-  commit.rss_date = make_rss_time_string(files[0].GetTime(), cfg)
-  if request.roottype == 'svn':
-    commit.rev = files[0].GetRevision()
-    commit.rss_url = request.get_url(view_func=view_revision,
-                                     params={'revision': commit.rev},
-                                     escape=1,
-                                     prefix=1)
-  else:
-    commit.rev = None
-    commit.rss_url = None
-
+  commit_rev = files[0].GetRevision()
   len_strip = len(dir_strip)
-
+  commit_files = []
+  num_allowed = 0
+  plus_count = 0
+  minus_count = 0
+  found_unreadable = 0
+  
   for f in files:
-    commit_time = f.GetTime()
-    if commit_time:
-      commit_time = make_time_string(commit_time, cfg)
-    change_type = f.GetTypeString()
-    rev = f.GetRevision()
-    rev_prev = prev_rev(rev)
-    
     dirname = f.GetDirectory()
     filename = f.GetFile()
     if dir_strip:
       assert dirname[:len_strip] == dir_strip
       assert len(dirname) == len_strip or dirname[len(dir_strip)] == '/'
       dirname = dirname[len_strip+1:]
-    filename = dirname and ("%s/%s" % (dirname, filename)) or filename
+    where = dirname and ("%s/%s" % (dirname, filename)) or filename
+    rev = f.GetRevision()
+    rev_prev = prev_rev(rev)
+    commit_time = f.GetTime()
+    if commit_time:
+      commit_time = make_time_string(commit_time, cfg)
+    change_type = f.GetTypeString()
 
     # In CVS, we can actually look at deleted revisions; in Subversion
     # we can't -- we'll look at the previous revision instead.
+    exam_rev = rev
+    if request.roottype == 'svn' and change_type == 'Remove':
+      exam_rev = rev_prev
+
+    # Check path access (since the commits database logic bypasses the
+    # vclib layer and, thus, the vcauth stuff that layer uses).
+    path_parts = _path_parts(where)
+    if path_parts:
+      # Skip files in CVSROOT if asked to hide such.
+      if cfg.options.hide_cvsroot \
+         and is_cvsroot_path(request.roottype, path_parts):
+        found_unreadable = 1
+        continue
+      
+      # We have to do a rare authz check here because this data comes
+      # from the CVSdb, not from the vclib providers.
+      #
+      # WARNING: The Subversion CVSdb integration logic is weak, weak,
+      # weak.  It has no ability to track copies, so complex
+      # situations like a copied directory with a deleted subfile (all
+      # in the same revision) are very ... difficult.  We've no choice
+      # but to omit as unauthorized paths the authorization logic
+      # can't find.
+      try:
+        readable = vclib.check_path_access(request.repos, path_parts,
+                                           None, exam_rev)
+      except vclib.ItemNotFound:
+        readable = 0
+      if not readable:
+        found_unreadable = 1
+        continue
+         
     if request.roottype == 'svn':
-      if change_type == 'Remove':
-        params = { 'pathrev': rev_prev }
-      else:
-        params = { 'pathrev': rev }
+      params = { 'pathrev': exam_rev }
     else:
-      params = { 'revision': rev, 'pathrev': f.GetBranch() or None }
-        
+      params = { 'revision': exam_rev, 'pathrev': f.GetBranch() or None }  
+    
     dir_href = request.get_url(view_func=view_directory,
                                where=dirname, pathtype=vclib.DIR,
                                params=params, escape=1)
     log_href = request.get_url(view_func=view_log,
-                               where=filename, pathtype=vclib.FILE,
+                               where=where, pathtype=vclib.FILE,
                                params=params, escape=1)
     diff_href = view_href = download_href = None
     if 'markup' in cfg.options.allowed_views:
       view_href = request.get_url(view_func=view_markup,
-                                  where=filename, pathtype=vclib.FILE,
+                                  where=where, pathtype=vclib.FILE,
                                   params=params, escape=1)
     if 'co' in cfg.options.allowed_views:
       download_href = request.get_url(view_func=view_checkout,
-                                      where=filename, pathtype=vclib.FILE,
+                                      where=where, pathtype=vclib.FILE,
                                       params=params, escape=1)
     if change_type == 'Change':
       diff_href_params = params.copy()
@@ -3584,27 +3546,29 @@
         'diff_format': None
         })
       diff_href = request.get_url(view_func=view_diff,
-                                  where=filename, pathtype=vclib.FILE,
+                                  where=where, pathtype=vclib.FILE,
                                   params=diff_href_params, escape=1)
-    prefer_markup = ezt.boolean(default_view(guess_mime(filename),
-                                             cfg) == view_markup)      
+    mime_type = calculate_mime_type(request, path_parts, exam_rev)
+    prefer_markup = ezt.boolean(default_view(mime_type, cfg) == view_markup)
 
-    # skip files in forbidden or hidden modules
-    dir_parts = filter(None, string.split(dirname, '/'))
-    if dir_parts \
-       and ((dir_parts[0] == 'CVSROOT'
-             and cfg.options.hide_cvsroot) \
-            or not request.auth.check_path_access(dir_parts)):
+    # Update plus/minus line change count.
+    plus = int(f.GetPlusCount())
+    minus = int(f.GetMinusCount())
+    plus_count = plus_count + plus
+    minus_count = minus_count + minus
+    
+    num_allowed = num_allowed + 1
+    if max_files and num_allowed > max_files:
       continue
 
-    commit.files.append(_item(date=commit_time,
+    commit_files.append(_item(date=commit_time,
                               dir=request.server.escape(dirname),
-                              file=request.server.escape(f.GetFile()),
+                              file=request.server.escape(filename),
                               author=request.server.escape(f.GetAuthor()),
                               rev=rev,
                               branch=f.GetBranch(),
-                              plus=int(f.GetPlusCount()),
-                              minus=int(f.GetMinusCount()),
+                              plus=plus,
+                              minus=minus,
                               type=change_type,
                               dir_href=dir_href,
                               log_href=log_href,
@@ -3612,6 +3576,42 @@
                               download_href=download_href,
                               prefer_markup=prefer_markup,
                               diff_href=diff_href))
+
+  # No files survived authz checks?  Let's just pretend this
+  # little commit didn't happen, shall we?
+  if not len(commit_files):
+    return None
+
+  commit = _item(num_files=len(commit_files), files=commit_files,
+                 plus=plus_count, minus=minus_count)
+  commit.limited_files = ezt.boolean(num_allowed > len(commit_files))
+
+  # We'll mask log messages in commits which contain unreadable paths,
+  # but even that is kinda iffy.  If a person searches for
+  # '/some/hidden/path' across log messages, then gets a response set
+  # that shows commits lacking log message, said person can reasonably
+  # assume that the log messages contained the hidden path, and that
+  # this is likely because they are referencing a real path in the
+  # repository -- a path the user isn't supposed to even know about.
+  if found_unreadable:
+    commit.log = None
+    commit.short_log = None
+  else:
+    commit.log = htmlify(desc)
+    commit.short_log = format_log(desc, cfg, format != 'rss')
+  commit.author = request.server.escape(author)
+  commit.rss_date = make_rss_time_string(date, request.cfg)
+  if request.roottype == 'svn':
+    commit.rev = commit_rev
+    commit.rss_url = '%s://%s%s' % \
+      (request.server.getenv("HTTPS") == "on" and "https" or "http",
+       request.server.getenv("HTTP_HOST"),
+       request.get_url(view_func=view_revision,
+                       params={'revision': commit.rev},
+                       escape=1))
+  else:
+    commit.rev = None
+    commit.rss_url = None
   return commit
 
 def query_backout(request, commits):
@@ -3752,47 +3752,49 @@
     current_desc = query.commit_list[0].GetDescriptionID()
     current_rev = query.commit_list[0].GetRevision()
     dir_strip = _path_join(repos_dir)
+
     for commit in query.commit_list:
-      # base modification time on the newest commit ...
+      commit_desc = commit.GetDescriptionID()
+      commit_rev = commit.GetRevision()
+
+      # base modification time on the newest commit
       if commit.GetTime() > mod_time:
         mod_time = commit.GetTime()
-      # form plus/minus totals
-      plus_count = plus_count + int(commit.GetPlusCount())
-      minus_count = minus_count + int(commit.GetMinusCount())
-      # group commits with the same commit message ...
-      desc = commit.GetDescriptionID()
+        
       # For CVS, group commits with the same commit message.
       # For Subversion, group them only if they have the same revision number
       if request.roottype == 'cvs':
-        if current_desc == desc:
-          if not limit_changes or len(files) < limit_changes:
-            files.append(commit)
-          else:
-            limited_files = 1
+        if current_desc == commit_desc:
+          files.append(commit)
           continue
       else:
-        if current_rev == commit.GetRevision():
-          if not limit_changes or len(files) < limit_changes:
-            files.append(commit)
-          else:
-            limited_files = 1
+        if current_rev == commit_rev:
+          files.append(commit)
           continue
 
-      # if our current group has any allowed files, append a commit
-      # with those files.
-      if len(files):
-        commits.append(build_commit(request, files, limited_files, dir_strip))
+      # append this grouping
+      commit_item = build_commit(request, files, limit_changes,
+                                 dir_strip, format)
+      if commit_item:
+        # update running plus/minus totals
+        plus_count = plus_count + commit_item.plus
+        minus_count = minus_count + commit_item.minus
+        commits.append(commit_item)
 
       files = [ commit ]
       limited_files = 0
-      current_desc = desc
-      current_rev = commit.GetRevision()
+      current_desc = commit_desc
+      current_rev = commit_rev
       
-    # we need to tack on our last commit grouping, but, again, only if
-    # it has allowed files.
-    if len(files):
-      commits.append(build_commit(request, files, limited_files, dir_strip))
-
+    # we need to tack on our last commit grouping, if any
+    commit_item = build_commit(request, files, limit_changes,
+                               dir_strip, format)
+    if commit_item:
+      # update running plus/minus totals
+      plus_count = plus_count + commit_item.plus
+      minus_count = minus_count + commit_item.minus
+      commits.append(commit_item)
+  
   # only show the branch column if we are querying all branches
   # or doing a non-exact branch match on a CVS repository.
   show_branch = ezt.boolean(request.roottype == 'cvs' and
@@ -3865,13 +3867,100 @@
 for code, view in _views.items():
   _view_codes[view] = code
 
-def list_roots(cfg):
+def list_roots(request):
+  cfg = request.cfg
   allroots = { }
+  
+  # Add the viewable Subversion roots
   for root in cfg.general.svn_roots.keys():
+    auth = setup_authorizer(cfg, request.username, root)
+    try:
+      vclib.svn.SubversionRepository(root, cfg.general.svn_roots[root], auth,
+                                     cfg.utilities, cfg.options.svn_config_dir)
+    except vclib.ReposNotFound:
+      continue
     allroots[root] = [cfg.general.svn_roots[root], 'svn']
+
+  # Add the viewable CVS roots
   for root in cfg.general.cvs_roots.keys():
+    auth = setup_authorizer(cfg, request.username, root)
+    try:
+      vclib.ccvs.CVSRepository(root, cfg.general.cvs_roots[root], auth,
+                               cfg.utilities, cfg.options.use_rcsparse)
+    except vclib.ReposNotFound:
+      continue
     allroots[root] = [cfg.general.cvs_roots[root], 'cvs']
+    
   return allroots
+
+def expand_root_parents(cfg):
+  """Expand the configured root parents into individual roots."""
+  
+  # Each item in root_parents is a "directory : repo_type" string.
+  for pp in cfg.general.root_parents:
+    pos = string.rfind(pp, ':')
+    if pos < 0:
+      raise debug.ViewVCException(
+        "The path '%s' in 'root_parents' does not include a "
+        "repository type." % (pp))
+
+    repo_type = string.strip(pp[pos+1:])
+    pp = os.path.normpath(string.strip(pp[:pos]))
+
+    if repo_type == 'cvs':
+      roots = vclib.ccvs.expand_root_parent(pp)
+      if cfg.options.hide_cvsroot and roots.has_key('CVSROOT'):
+        del roots['CVSROOT']
+      cfg.general.cvs_roots.update(roots)
+    elif repo_type == 'svn':
+      roots = vclib.svn.expand_root_parent(pp)
+      cfg.general.svn_roots.update(roots)
+    else:
+      raise debug.ViewVCException(
+        "The path '%s' in 'root_parents' has an unrecognized "
+        "repository type." % (pp))
+
+def find_root_in_parents(cfg, rootname, roottype):
+  """Return the rootpath for configured ROOTNAME of ROOTTYPE."""
+
+  # Easy out:  caller wants rootname "CVSROOT", and we're hiding those.
+  if rootname == 'CVSROOT' and cfg.options.hide_cvsroot:
+    return None
+  
+  for pp in cfg.general.root_parents:
+    pos = string.rfind(pp, ':')
+    if pos < 0:
+      continue
+    repo_type = string.strip(pp[pos+1:])
+    if repo_type != roottype:
+      continue
+    pp = os.path.normpath(string.strip(pp[:pos]))
+    
+    if roottype == 'cvs':
+      roots = vclib.ccvs.expand_root_parent(pp)
+    elif roottype == 'svn':
+      roots = vclib.svn.expand_root_parent(pp)
+    else:
+      roots = {}
+    if roots.has_key(rootname):
+      return roots[rootname]
+  return None
+
+def locate_root(cfg, rootname):
+  """Return a 2-tuple ROOTTYPE, ROOTPATH for configured ROOTNAME."""
+  if cfg.general.cvs_roots.has_key(rootname):
+    return 'cvs', cfg.general.cvs_roots[rootname]
+  path_in_parent = find_root_in_parents(cfg, rootname, 'cvs')
+  if path_in_parent:
+    cfg.general.cvs_roots[rootname] = path_in_parent
+    return 'cvs', path_in_parent
+  if cfg.general.svn_roots.has_key(rootname):
+    return 'svn', cfg.general.svn_roots[rootname]
+  path_in_parent = find_root_in_parents(cfg, rootname, 'svn')
+  if path_in_parent:
+    cfg.general.svn_roots[rootname] = path_in_parent
+    return 'svn', path_in_parent
+  return None, None
   
 def load_config(pathname=None, server=None):
   debug.t_start('load-config')
@@ -3889,50 +3978,8 @@
   # load mime types file
   if cfg.general.mime_types_file:
     mimetypes.init([cfg.general.mime_types_file])
-
-  # special handling for root_parents.  Each item in root_parents is
-  # a "directory : repo_type" string.  For each item in
-  # root_parents, we get a list of the subdirectories.
-  #
-  # If repo_type is "cvs", and the subdirectory contains a child
-  # "CVSROOT/config", then it is added to cvs_roots. Or, if the
-  # root directory itself contains a child "CVSROOT/config" file,
-  # then all its subdirectories are added to cvs_roots.
-  #
-  # If repo_type is "svn", and the subdirectory contains a child
-  # "format", then it is added to svn_roots.
-  for pp in cfg.general.root_parents:
-    pos = string.rfind(pp, ':')
-    if pos < 0:
-      raise debug.ViewVCException(
-        "The path '%s' in 'root_parents' does not include a "
-        "repository type." % pp)
-
-    repo_type = string.strip(pp[pos+1:])
-    pp = os.path.normpath(string.strip(pp[:pos]))
-
-    try:
-      subpaths = os.listdir(pp)
-    except OSError:
-      raise debug.ViewVCException(
-        "The path '%s' in 'root_parents' does not refer to "
-        "a valid directory." % pp)
-
-    cvsroot = os.path.exists(os.path.join(pp, "CVSROOT", "config"))
-
-    for subpath in subpaths:
-      if os.path.exists(os.path.join(pp, subpath)):
-        if (repo_type == 'cvs'
-            and (os.path.exists(os.path.join(pp, subpath, "CVSROOT", "config"))
-                 or (cvsroot and (subpath != 'CVSROOT'
-                                  or not cfg.options.hide_cvsroot)))):
-          cfg.general.cvs_roots[subpath] = os.path.join(pp, subpath)
-        elif repo_type == 'svn' and \
-             os.path.exists(os.path.join(pp, subpath, "format")):
-          cfg.general.svn_roots[subpath] = os.path.join(pp, subpath)
-
+  
   debug.t_end('load-config')
-
   return cfg
 
 

Modified: trunk/notes/releases.txt
==============================================================================
--- trunk/notes/releases.txt	(original)
+++ trunk/notes/releases.txt	Mon Nov  3 09:57:02 2008
@@ -65,12 +65,14 @@
     archive files into the root directory of the viewvc.org website
     (unversioned).
 
-12. Update the websites (both the viewvc.org/ and www/ ones) to refer
-    to the new release files.
+12. On trunk, update the websites (both the viewvc.org/ and www/ ones)
+    to refer to the new release files, and copy the CHANGES for the
+    new release into trunk's CHANGES file.
 
 13. Edit the file 'lib/viewvc.py' again, re-adding the "-dev" suffix
     and incrementing the patch number assigned to the __version__
-    variable, and commit:
+    variable, and add a new empty block in the branch's CHANGES file,
+    and commit:
 
        svn ci -m "Begin a new release cycle."
 

Modified: trunk/scripts/last-merge-tag
==============================================================================
--- trunk/scripts/last-merge-tag	(original)
+++ trunk/scripts/last-merge-tag	Mon Nov  3 09:57:02 2008
@@ -1 +1 @@
-viewcvs-2007-11-25
+viewcvs-2008-11-03

Modified: trunk/templates-contrib/tabbed/templates/annotate.ezt
==============================================================================
--- trunk/templates-contrib/tabbed/templates/annotate.ezt	(original)
+++ trunk/templates-contrib/tabbed/templates/annotate.ezt	Mon Nov  3 09:57:02 2008
@@ -3,19 +3,7 @@
   [define help_href][docroot]/help_rootview.html[end]
 [# end]
 [include "include/header.ezt" "annotate"]
-
-<table class="auto">
-<tr>
-<td>Revision:</td>
-<td><strong>[if-any revision_href]<a href="[revision_href]">[rev]</a>[else][rev][end]</strong></td>
-</tr>
-[if-any orig_path]
-<tr>
-<td>Original Path:</td>
-<td><strong><a href="[orig_href]"><em>[orig_path]</em></a></strong></td>
-</tr>
-[end]
-</table>
+[include "include/fileview.ezt"]
 
 <div id="vc_main_body">
 <!-- ************************************************************** -->
@@ -50,6 +38,8 @@
 [end]
 </table>
 
+[include "include/props.ezt"]
+
 <!-- ************************************************************** -->
 </div>
 

Modified: trunk/templates-contrib/tabbed/templates/diff.ezt
==============================================================================
--- trunk/templates-contrib/tabbed/templates/diff.ezt	(original)
+++ trunk/templates-contrib/tabbed/templates/diff.ezt	Mon Nov  3 09:57:02 2008
@@ -6,7 +6,7 @@
 
 <form method="get" action="[diff_format_action]" style="display: inline;">
   <div>
-    [diff_format_hidden_values]
+    [for diff_format_hidden_values]<input type="hidden" name="[diff_format_hidden_values.name]" value="[diff_format_hidden_values.value]"/>[end]
     <select name="diff_format" onchange="submit()">
       <option value="h" [is diff_format "h"]selected="selected"[end]>Colored Diff</option>
       <option value="l" [is diff_format "l"]selected="selected"[end]>Long Colored Diff</option>

Modified: trunk/templates-contrib/tabbed/templates/directory.ezt
==============================================================================
--- trunk/templates-contrib/tabbed/templates/directory.ezt	(original)
+++ trunk/templates-contrib/tabbed/templates/directory.ezt	Mon Nov  3 09:57:02 2008
@@ -128,4 +128,5 @@
 
 </table>
 
+[include "include/props.ezt"]
 [include "include/dir_footer.ezt"]

Modified: trunk/templates-contrib/tabbed/templates/docroot/styles.css
==============================================================================
--- trunk/templates-contrib/tabbed/templates/docroot/styles.css	(original)
+++ trunk/templates-contrib/tabbed/templates/docroot/styles.css	Mon Nov  3 09:57:02 2008
@@ -19,6 +19,7 @@
   border: none;
 }
 tr, td, th { vertical-align: top; }
+th { white-space: nowrap; }
 table.auto {
     width: auto;
 }
@@ -131,6 +132,15 @@
 }
 
 
+/*** Properties Listing ***/
+.vc_properties {
+  margin: 1em 0;
+}
+.vc_properties h2 {
+  font-size: 115%;
+}
+
+
 /*** Markup Summary Header ***/
 .vc_summary {
   background-color: #eeeeee;

Modified: trunk/templates-contrib/tabbed/templates/include/diff_form.ezt
==============================================================================
--- trunk/templates-contrib/tabbed/templates/include/diff_form.ezt	(original)
+++ trunk/templates-contrib/tabbed/templates/include/diff_form.ezt	Mon Nov  3 09:57:02 2008
@@ -17,7 +17,7 @@
   <tr>
   <td>&nbsp;</td>
   <td>
-  [diff_select_hidden_values]
+  [for diff_select_hidden_values]<input type="hidden" name="[diff_select_hidden_values.name]" value="[diff_select_hidden_values.value]"/>[end]
   Diffs between
 [if-any tags]
   <select name="r1">

Modified: trunk/templates-contrib/tabbed/templates/include/dir_footer.ezt
==============================================================================
--- trunk/templates-contrib/tabbed/templates/include/dir_footer.ezt	(original)
+++ trunk/templates-contrib/tabbed/templates/include/dir_footer.ezt	Mon Nov  3 09:57:02 2008
@@ -9,7 +9,7 @@
           <td>
             <form method="get" action="[search_re_action]">
               <div>
-                [search_re_hidden_values]
+                [for search_re_hidden_values]<input type="hidden" name="[search_re_hidden_values.name]" value="[search_re_hidden_values.value]"/>[end]
                 <input type="text" name="search" value="[search_re]" />
                 <input type="submit" value="Show" />
               </div>
@@ -22,7 +22,7 @@
           <td>
             <form method="get" action="[search_tag_action]">
               <div>
-                [search_tag_hidden_values]
+                [for search_tag_hidden_values]<input type="hidden" name="[search_tag_hidden_values.name]" value="[search_tag_hidden_values.value]"/>[end]
                 <input type="submit" value="Show all files" />
               </div>
             </form>

Modified: trunk/templates-contrib/tabbed/templates/include/dir_header.ezt
==============================================================================
--- trunk/templates-contrib/tabbed/templates/include/dir_header.ezt	(original)
+++ trunk/templates-contrib/tabbed/templates/include/dir_header.ezt	Mon Nov  3 09:57:02 2008
@@ -40,22 +40,24 @@
   <td><a href="[queryform_href]">Query revision history</a></td>
 </tr>
 [end]
-</table>
-  [is cfg.options.use_pagesize "0"]
-  [else]
-    [is picklist_len "1"]
-    [else]
-      <form method="get" action="[dir_paging_action]">
-        [dir_paging_hidden_values]
-        <input type="submit" value="Go to:" />
+
+[is cfg.options.use_pagesize "0"][else][is picklist_len "1"][else]
+<tr>
+  <td>Jump to page:</td>
+  <td><form method="get" action="[dir_paging_action]">
+        [for dir_paging_hidden_values]<input type="hidden" name="[dir_paging_hidden_values.name]" value="[dir_paging_hidden_values.value]"/>[end]
         <select name="dir_pagestart"  onchange="submit()">
           [for picklist]
             <option [is picklist.count dir_pagestart]selected[end] value="[picklist.count]">Page [picklist.page]: [picklist.start] to [picklist.end]</option>
           [end]
         </select>
+        <input type="submit" value="Go" />
       </form>
-    [end]
-  [end]
+  </td>
+</tr>
+[end][end]
+
+</table>
 
 <div id="vc_main_body">
 <!-- ************************************************************** -->

Modified: trunk/templates-contrib/tabbed/templates/include/header.ezt
==============================================================================
--- trunk/templates-contrib/tabbed/templates/include/header.ezt	(original)
+++ trunk/templates-contrib/tabbed/templates/include/header.ezt	Mon Nov  3 09:57:02 2008
@@ -22,43 +22,10 @@
      width="150" height="60" /></a>
 </div>
 
-[if-any roots]
-  <form method="get" style="display: inline" action="[change_root_action]">
-    [change_root_hidden_values]
-      <strong>Repository:</strong>
-      <select name="root" onchange="submit()">
-        [define cvs_root_options][end]
-        [define svn_root_options][end]
-        <option value="*viewroots*"[is view "roots"] selected="selected"[else][end]>Repository Listing</option>
-        [for roots]
-          [define root_option][end]
-          [is roots.name rootname]
-            [define root_option]<option selected="selected">[roots.name]</option>[end]
-          [else]
-            [define root_option]<option>[roots.name]</option>[end]
-          [end]
-          [is roots.type "cvs"]
-            [define cvs_root_options][cvs_root_options][root_option][end]
-          [else]
-            [is roots.type "svn"]
-              [define svn_root_options][svn_root_options][root_option][end]
-            [end]
-          [end]
-        [end]
-        [is cvs_root_options ""][else]
-          <optgroup label="CVS Repositories">[cvs_root_options]</optgroup>
-        [end]
-        [is svn_root_options ""][else]
-          <optgroup label="Subversion Repositories">[svn_root_options]</optgroup>
-        [end]
-      </select>
-      <input type="submit" value="Go" />
-  </form>
-[end]
-
 <div id="vc_current_path">
 <strong>[page_title]</strong>
 [if-any nav_path]
+  [if-any roots_href]<a href="[roots_href]">/</a>[else]/[end]
   [for nav_path]
     [if-any nav_path.href]<a href="[nav_path.href]">[end]
       [nav_path.name][if-any nav_path.href]</a>[end]

Modified: trunk/templates-contrib/tabbed/templates/include/log_footer.ezt
==============================================================================
--- trunk/templates-contrib/tabbed/templates/include/log_footer.ezt	(original)
+++ trunk/templates-contrib/tabbed/templates/include/log_footer.ezt	Mon Nov  3 09:57:02 2008
@@ -1,13 +1,9 @@
 <!-- ************************************************************** -->
 </div>
 
-[include "paging.ezt"]
-
 [is pathtype "file"]
   [include "diff_form.ezt"]
 [end]
 
-[include "sort.ezt"]
-
 [include "footer.ezt"]
 

Modified: trunk/templates-contrib/tabbed/templates/include/log_header.ezt
==============================================================================
--- trunk/templates-contrib/tabbed/templates/include/log_header.ezt	(original)
+++ trunk/templates-contrib/tabbed/templates/include/log_header.ezt	Mon Nov  3 09:57:02 2008
@@ -45,7 +45,42 @@
   <td>[include "pathrev_form.ezt"]</td>
 </tr>
 
-[include "paging.ezt"]
+[is cfg.options.use_pagesize "0"][else][is picklist_len "1"][else]    
+<tr>
+  <td>Jump to page:</td>
+  <td><form method="get" action="[log_paging_action]">
+        [for log_paging_hidden_values]<input type="hidden" name="[log_paging_hidden_values.name]" value="[log_paging_hidden_values.value]"/>[end]
+        <select name="log_pagestart"  onchange="submit()">
+          [for picklist]
+           [if-any picklist.more]
+            <option [is picklist.count log_pagestart]selected[end] value="[picklist.count]">Page [picklist.page]: [picklist.start] ...</option>
+           [else]
+            <option [is picklist.count log_pagestart]selected[end] value="[picklist.count]">Page [picklist.page]: [picklist.start] - [picklist.end]</option>
+           [end]
+          [end]
+        </select>
+        <input type="submit" value="Go" />
+      </form>
+  </td>
+</tr>
+[end][end]
+
+<tr>
+  <td>Sort logs by:</td>
+  <td><form method="get" action="[logsort_action]">
+      <div>
+        <a name="logsort"></a>
+          [for logsort_hidden_values]<input type="hidden" name="[logsort_hidden_values.name]" value="[logsort_hidden_values.value]"/>[end]
+          <select name="logsort" onchange="submit()">
+            <option value="cvs" [is logsort "cvs"]selected="selected"[end]>Not sorted</option>
+            <option value="date" [is logsort "date"]selected="selected"[end]>Commit date</option>
+            <option value="rev" [is logsort "rev"]selected="selected"[end]>Revision</option>
+          </select>
+          <input type="submit" value="  Sort  " />
+      </div>
+    </form>
+  </td>
+</tr>
 
 </table>
 

Modified: trunk/templates-contrib/tabbed/templates/include/pathrev_form.ezt
==============================================================================
--- trunk/templates-contrib/tabbed/templates/include/pathrev_form.ezt	(original)
+++ trunk/templates-contrib/tabbed/templates/include/pathrev_form.ezt	Mon Nov  3 09:57:02 2008
@@ -1,6 +1,6 @@
 <form method="get" action="[pathrev_action]" style="display: inline">
 <div style="display: inline">
-[pathrev_hidden_values]
+[for pathrev_hidden_values]<input type="hidden" name="[pathrev_hidden_values.name]" value="[pathrev_hidden_values.value]"/>[end]
 [is roottype "cvs"]
   [define pathrev_selected][pathrev][end]
   <select name="pathrev" onchange="submit()">
@@ -41,7 +41,7 @@
 [if-any pathrev]
 <form method="get" action="[pathrev_clear_action]" style="display: inline">
 <div style="display: inline">
-[pathrev_clear_hidden_values]
+[for pathrev_clear_hidden_values]<input type="hidden" name="[pathrev_clear_hidden_values.name]" value="[pathrev_clear_hidden_values.value]"/>[end]
 [if-any lastrev]
   [is pathrev lastrev][else]<input type="submit" value="Set to [lastrev]" />[end]
   (<i>Current path doesn't exist after revision <strong>[lastrev]</strong></i>)

Modified: trunk/templates-contrib/tabbed/templates/log.ezt
==============================================================================
--- trunk/templates-contrib/tabbed/templates/log.ezt	(original)
+++ trunk/templates-contrib/tabbed/templates/log.ezt	Mon Nov  3 09:57:02 2008
@@ -91,6 +91,10 @@
     [end]
   [end]
 
+  [if-any entries.lockinfo]
+    <br />Lock status: <img src="[docroot]/images/lock.png" alt="Locked" width="16" height="16" /> [entries.lockinfo]
+  [end]
+
   [is roottype "svn"]
     [if-any entries.size]
     <br />File length: [entries.size] byte(s)

Modified: trunk/templates-contrib/tabbed/templates/markup.ezt
==============================================================================
--- trunk/templates-contrib/tabbed/templates/markup.ezt	(original)
+++ trunk/templates-contrib/tabbed/templates/markup.ezt	Mon Nov  3 09:57:02 2008
@@ -3,71 +3,15 @@
   [define help_href][docroot]/help_rootview.html[end]
 [# end]
 [include "include/header.ezt" "markup"]
-
-<table class="auto">
-<tr>
-<td>Revision:</td>
-<td><strong>[if-any revision_href]<a href="[revision_href]">[rev]</a>[else][rev][end]</strong> [if-any vendor_branch] <em>(vendor branch)</em>[end]</td> 
-</tr>
-<tr>
-<tr>
-<td>Committed:</td>
-<td>[if-any date]<em>[date]</em> [end][if-any ago]([ago] ago) [end][if-any author]by <em>[author]</em>[end]</td>
-</tr>
-[if-any orig_path]
-<tr>
-<td>Original Path:</td>
-<td><strong><a href="[orig_href]"><em>[orig_path]</em></a></strong></td>
-</tr>
-[end]
-[if-any branches]
-<tr>
-<td>Branch:</td>
-<td><strong>[branches]</strong></td>
-</tr>
-[end]
-[if-any tags]
-<tr>
-<td>CVS Tags:</td>
-<td><strong>[tags]</strong></td>
-</tr>
-[end]
-[if-any branch_points]
-<tr>
-<td>Branch point for:</td>
-<td><strong>[branch_points]</strong></td>
-</tr>
-[end]
-[is roottype "cvs"][if-any changed]
-<tr>
-<td>Changes since <strong>[prev]</strong>:</td>
-<td><strong>[changed] lines</strong></td>
-</tr>
-[end][end]
-[is roottype "svn"][if-any size]
-<td>File size:</td>
-<td>[size] byte(s)</td>
-</tr>
-[end][end]
-[is state "dead"]
-<tr>
-<td>State:</td>
-<td><strong><em>FILE REMOVED</em></strong></td>
-</tr>
-[end]
-[if-any log]
-<tr>
-<td>Log Message:</td>
-<td><pre class="vc_log">[log]</span></td>
-</tr>
-[end]
-</table>
+[include "include/fileview.ezt"]
 
 <div id="vc_main_body">
 <!-- ************************************************************** -->
 
 <div id="vc_markup"><pre>[markup]</pre></div>
 
+[include "include/props.ezt"]
+
 <!-- ************************************************************** -->
 </div>
 

Modified: trunk/templates-contrib/tabbed/templates/query_form.ezt
==============================================================================
--- trunk/templates-contrib/tabbed/templates/query_form.ezt	(original)
+++ trunk/templates-contrib/tabbed/templates/query_form.ezt	Mon Nov  3 09:57:02 2008
@@ -7,7 +7,7 @@
 <form action="[query_action]" method="get">
 
 <div class="vc_query_form">
-  [query_hidden_values]
+  [for query_hidden_values]<input type="hidden" name="[query_hidden_values.name]" value="[query_hidden_values.value]"/>[end]
 <table cellspacing="0" cellpadding="5" class="auto">
   [is roottype "cvs"]
   [# For subversion, the branch field is not used ]

Modified: trunk/templates-contrib/tabbed/templates/query_results.ezt
==============================================================================
--- trunk/templates-contrib/tabbed/templates/query_results.ezt	(original)
+++ trunk/templates-contrib/tabbed/templates/query_results.ezt	Mon Nov  3 09:57:02 2008
@@ -1,5 +1,5 @@
 [# setup page definitions]
-  [define page_title]Query results on /[where][end]
+  [define page_title]Query results in:[end]
   [define help_href][docroot]/help_rootview.html[end]
 [# end]
 

Modified: trunk/templates-contrib/tabbed/templates/revision.ezt
==============================================================================
--- trunk/templates-contrib/tabbed/templates/revision.ezt	(original)
+++ trunk/templates-contrib/tabbed/templates/revision.ezt	Mon Nov  3 09:57:02 2008
@@ -9,7 +9,7 @@
   <tr align="left">
     <th>Jump to revision:</th>
     <td>
-      [jump_rev_hidden_values]
+      [for jump_rev_hidden_values]<input type="hidden" name="[jump_rev_hidden_values.name]" value="[jump_rev_hidden_values.value]"/>[end]
       <input type="text" name="revision" value="[rev]" />
       <input type="submit" value="Go" />
       [if-any prev_href]

Modified: trunk/templates/diff.ezt
==============================================================================
--- trunk/templates/diff.ezt	(original)
+++ trunk/templates/diff.ezt	Mon Nov  3 09:57:02 2008
@@ -196,7 +196,7 @@
     <td>
       <form method="get" action="[diff_format_action]">
         <div>
-          [diff_format_hidden_values]
+          [for diff_format_hidden_values]<input type="hidden" name="[diff_format_hidden_values.name]" value="[diff_format_hidden_values.value]"/>[end]
           <select name="diff_format" onchange="submit()">
             <option value="h" [is diff_format "h"]selected="selected"[end]>Colored Diff</option>
             <option value="l" [is diff_format "l"]selected="selected"[end]>Long Colored Diff</option>

Modified: trunk/templates/dir_new.ezt
==============================================================================
--- trunk/templates/dir_new.ezt	(original)
+++ trunk/templates/dir_new.ezt	Mon Nov  3 09:57:02 2008
@@ -43,6 +43,7 @@
     <a name="[entries.anchor]" href="[is entries.pathtype "dir"][entries.view_href][else][if-any entries.prefer_markup][entries.view_href][else][entries.download_href][end][end]" title="[is entries.pathtype "dir"]View Directory Contents[else][if-any entries.prefer_markup]View[else]Download[end] File Contents[end]">
        <img src="[docroot]/images/[is entries.pathtype "dir"]dir[else][is entries.state "dead"]broken[else]text[end][end].png" alt="" class="vc_icon" />
        [entries.name][is entries.pathtype "dir"]/[end]</a>
+       [if-any entries.lockinfo]<img src="[docroot]/images/lock.png" alt="locked" class="vc_icon" title="Locked by [entries.lockinfo]" />[end]
        [is entries.state "dead"](dead)[end]
     </td>
 

Modified: trunk/templates/directory.ezt
==============================================================================
--- trunk/templates/directory.ezt	(original)
+++ trunk/templates/directory.ezt	Mon Nov  3 09:57:02 2008
@@ -97,7 +97,9 @@
        <td>&nbsp;[if-any entries.rev]<a href="[entries.log_href]" title="View directory revision log"><strong>[entries.rev]</strong></a>[end]</td>
        [else]
        [define rev_href][if-any entries.prefer_markup][entries.view_href][else][if-any entries.download_href][entries.download_href][end][end][end]
-       <td>&nbsp;[if-any entries.rev][if-any rev_href]<a href="[rev_href]" title="[if-any entries.prefer_markup]View[else]Download[end] file contents">[end]<strong>[entries.rev]</strong>[if-any rev_href]</a>[end][end]</td>
+       <td style="white-space: nowrap;">&nbsp;[if-any entries.rev][if-any rev_href]<a href="[rev_href]" title="[if-any entries.prefer_markup]View[else]Download[end] file contents">[end]<strong>[entries.rev]</strong>[if-any rev_href]</a>[end][end]
+       [if-any entries.lockinfo]<img src="[docroot]/images/lock.png" alt="locked" class="vc_icon" title="Locked by [entries.lockinfo]" />[end]
+       </td>
        [end]
        <td>&nbsp;[entries.ago]</td>
        <td>&nbsp;[entries.author]</td>

Modified: trunk/templates/docroot/help_dirview.html
==============================================================================
--- trunk/templates/docroot/help_dirview.html	(original)
+++ trunk/templates/docroot/help_dirview.html	Mon Nov  3 09:57:02 2008
@@ -4,31 +4,23 @@
 <head>
   <title>ViewVC Help: Directory View</title>
   <link rel="stylesheet" href="help.css" type="text/css" />
+  <link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon" />
+  <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
 </head>
 <body>
+  <div><a href="http://viewvc.org/index.html";><img src="images/viewvc-logo.png" alt="ViewVC logotype" /></a></div>
   <table>
     <col class="menu" />
     <col />
-    <tr>
-      <td><a href="http://viewvc.org/index.html";><img
-	     src="images/logo.png" alt="ViewVC logotype" /></a>
-      </td>
-      <td>
-	<h1>ViewVC Help: Directory View</h1>
-      </td>
-    </tr>
     <tr><td>
        <h3>Help</h3>
        <a href="help_rootview.html">General</a><br />
        <strong>Directory&nbsp;View</strong><br />
        <a href="help_log.html">Log&nbsp;View</a><br />
+       <a href="help_query.html">Query&nbsp;Database</a><br />
+    </td><td>
 
-       <h3>Internet</h3>
-       <a href="http://viewvc.org/index.html";>Home</a><br />
-       <a href="http://viewvc.org/upgrading.html";>Upgrading</a><br />
-       <a href="http://viewvc.org/contributing.html";>Contributing</a><br />
-       <a href="http://viewvc.org/license-1.html";>License</a><br />
-    </td><td colspan="2">
+    <h1>ViewVC Help: Directory View</h1>
 
     <p>The directory listing view should be a familiar sight to any
     computer user. It shows the path of the current directory being viewed

Modified: trunk/templates/docroot/help_log.html
==============================================================================
--- trunk/templates/docroot/help_log.html	(original)
+++ trunk/templates/docroot/help_log.html	Mon Nov  3 09:57:02 2008
@@ -4,32 +4,24 @@
 <head>
   <title>ViewVC Help: Log View</title>
   <link rel="stylesheet" href="help.css" type="text/css" />
+  <link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon" />
   <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
 </head>
 <body>
+  <div><a href="http://viewvc.org/index.html";><img src="images/viewvc-logo.png" alt="ViewVC logotype" /></a></div>
   <table>
     <col class="menu" />
     <col />
-    <tr>
-      <td><a href="http://viewvc.org/index.html";><img
-	     src="images/logo.png" alt="ViewVC logotype" /></a>
-      </td>
-      <td>
-	<h1>ViewVC Help: Log View</h1>
-      </td>
-    </tr>
     <tr><td>
        <h3>Help</h3>
        <a href="help_rootview.html">General</a><br />
        <a href="help_dirview.html">Directory&nbsp;View</a><br />
        <strong>Log&nbsp;View</strong><br />
+       <a href="help_query.html">Query&nbsp;Database</a><br />
+    </td><td>
+
+    <h1>ViewVC Help: Log View</h1>
 
-       <h3>Internet</h3>
-       <a href="http://viewvc.org/index.html";>Home</a><br />
-       <a href="http://viewvc.org/upgrading.html";>Upgrading</a><br />
-       <a href="http://viewvc.org/contributing.html";>Contributing</a><br />
-       <a href="http://viewvc.org/license-1.html";>License</a><br />
-    </td><td colspan="2">
     <p>
       The log view displays the revision history of the selected source
       file or directory. For each revision the following information is

Modified: trunk/templates/docroot/help_query.html
==============================================================================
--- trunk/templates/docroot/help_query.html	(original)
+++ trunk/templates/docroot/help_query.html	Mon Nov  3 09:57:02 2008
@@ -4,64 +4,59 @@
 <head>
   <title>ViewVC Help: Query The Commit Database</title>
   <link rel="stylesheet" href="help.css" type="text/css" />
+  <link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon" />
+  <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
 </head>
 <body>
-<table>
-  <col class="menu" />
-  <col />
-  <tr>
-    <td><a href=".."><img
-        src="images/logo.png" alt="ViewVC logotype" /></a>
-    </td>
-    <td><h1>ViewVC Help: Query The Commit Database</h1></td>
-  </tr>
-  <tr><td>
-     <h3>Other&nbsp;Help:</h3>
-     <a href="help_rootview.html">General</a><br />
-     <a href="help_dirview.html">Directory&nbsp;View</a><br />
-     <a href="help_log.html">Classic&nbsp;Log&nbsp;View</a><br />
-     <a href="help_logtable.html">Alternative&nbsp;Log&nbsp;View</a><br />
-     <strong>Query&nbsp;Database</strong>
-
-     <h3>Internet</h3>
-     <a href="http://viewvc.org/index.html";>Home</a><br />
-     <a href="http://viewvc.org/upgrading.html";>Upgrading</a><br />
-     <a href="http://viewvc.org/contributing.html";>Contributing</a><br />
-     <a href="http://viewvc.org/license-1.html";>License</a><br />
-  </td><td colspan="2">
-
-  <p> 
-    Select your parameters for querying the CVS commit database in the
-    form at the top of the page.  You
-    can search for multiple matches by typing a comma-seperated list
-    into the text fields.  Regular expressions, and wildcards are also
-    supported.  Blank text input fields are treated as wildcards.
-  </p>
-  <p>
-    Any of the text entry fields can take a comma-seperated list of
-    search arguments.  For example, to search for all commits from
-    authors <em>jpaint</em> and <em>gstein</em>, just type: <code>jpaint,
-    gstein</code> in the <em>Author</em> input box.  If you are searching
-    for items containing spaces or quotes, you will need to quote your
-    request.  For example, the same search above with quotes is:
-    <code>"jpaint", "gstein"</code>.
-  </p>
-  <p>                           
-    Wildcard and regular expression searches are entered in a similar
-    way to the quoted requests.  You must quote any wildcard or
-    regular expression request, and a command character preceeds the
-    first quote.  The command character <code>l</code>(lowercase L) is for wildcard
-    searches, and the wildcard character is a percent (<code>%</code>).  The
-    command character for regular expressions is <code>r</code>, and is
-    passed directly to MySQL, so you'll need to refer to the MySQL
-    manual for the exact regex syntax.  It is very similar to Perl.  A
-    wildard search for all files with a <em>.py</em> extention is:
-    <code>l"%.py"</code> in the <em>File</em> input box.  The same search done
-    with a regular expression is: <code>r".*\.py"</code>.
-  </p>
-  <p>                  
-    All search types can be mixed, as long as they are seperated by
-    commas.
-  </p>                                                    
-  </td></tr></table>
-</body></html>
+  <div><a href="http://viewvc.org/index.html";><img src="images/viewvc-logo.png" alt="ViewVC logotype" /></a></div>
+  <table>
+    <col class="menu" />
+    <col />
+    <tr><td>
+       <h3>Help:</h3>
+       <a href="help_rootview.html">General</a><br />
+       <a href="help_dirview.html">Directory&nbsp;View</a><br />
+       <a href="help_log.html">Log&nbsp;View</a><br />
+       <strong>Query&nbsp;Database</strong>
+    </td><td>
+  
+    <h1>ViewVC Help: Query The Commit Database</h1>
+  
+    <p> 
+      Select your parameters for querying the CVS commit database in the
+      form at the top of the page.  You
+      can search for multiple matches by typing a comma-seperated list
+      into the text fields.  Regular expressions, and wildcards are also
+      supported.  Blank text input fields are treated as wildcards.
+    </p>
+    <p>
+      Any of the text entry fields can take a comma-seperated list of
+      search arguments.  For example, to search for all commits from
+      authors <em>jpaint</em> and <em>gstein</em>, just type: <code>jpaint,
+      gstein</code> in the <em>Author</em> input box.  If you are searching
+      for items containing spaces or quotes, you will need to quote your
+      request.  For example, the same search above with quotes is:
+      <code>"jpaint", "gstein"</code>.
+    </p>
+    <p>                           
+      Wildcard and regular expression searches are entered in a similar
+      way to the quoted requests.  You must quote any wildcard or
+      regular expression request, and a command character preceeds the
+      first quote.  The command character <code>l</code>(lowercase L) is for wildcard
+      searches, and the wildcard character is a percent (<code>%</code>).  The
+      command character for regular expressions is <code>r</code>, and is
+      passed directly to MySQL, so you'll need to refer to the MySQL
+      manual for the exact regex syntax.  It is very similar to Perl.  A
+      wildard search for all files with a <em>.py</em> extention is:
+      <code>l"%.py"</code> in the <em>File</em> input box.  The same search done
+      with a regular expression is: <code>r".*\.py"</code>.
+    </p>
+    <p>                  
+      All search types can be mixed, as long as they are seperated by
+      commas.
+    </p>                                                    
+    </td></tr></table>
+  <hr />
+  <address><a href="mailto:users viewvc tigris org">ViewVC Users Mailinglist</a></address>
+  </body>
+</html>

Modified: trunk/templates/docroot/help_rootview.html
==============================================================================
--- trunk/templates/docroot/help_rootview.html	(original)
+++ trunk/templates/docroot/help_rootview.html	Mon Nov  3 09:57:02 2008
@@ -4,164 +4,152 @@
 <head>
   <title>ViewVC Help: General</title>
   <link rel="stylesheet" href="help.css" type="text/css" />
+  <link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon" />
+  <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
 </head>
 <body>
+  <div><a href="http://viewvc.org/index.html";><img src="images/viewvc-logo.png" alt="ViewVC logotype" /></a></div>
   <table>
     <col class="menu" />
     <col />
-    <tr>
-      <td><a href=".."><img
-	     src="images/logo.png" alt="ViewVC logotype" /></a>
-      </td>
-      <td>
-	<h1>ViewVC Help: General</h1>
-      </td>
-    </tr>
     <tr><td>
        <h3>Help</h3>
        <strong>General</strong><br />
        <a href="help_dirview.html">Directory&nbsp;View</a><br />
        <a href="help_log.html">Log&nbsp;View</a><br />
+       <a href="help_query.html">Query&nbsp;Database</a><br />
+    </td><td>
 
-       <h3>Internet</h3>
-       <a href="http://viewvc.org/index.html";>Home</a><br />
-       <a href="http://viewvc.org/upgrading.html";>Upgrading</a><br />
-       <a href="http://viewvc.org/contributing.html";>Contributing</a><br />
-       <a href="http://viewvc.org/license-1.html";>License</a><br />
-    </td><td colspan="2">
-
-  <p><em>ViewVC</em> is a WWW interface for CVS and Subversion
-  repositories. It allows you to browse the files and directories in a
-  repository while showing you metadata from the repository history: log
-  messages, modification dates, author names, revision numbers, copy
-  history, and so on. It provides several different views of repository
-  data to help you find the information you are looking for:</p>
-
-  <ul>
-    <li><a name="view-dir" href="help_dirview.html"><strong>Directory
-    View</strong></a> - Shows a list of files and subdirectories in a
-    directory of the repository, along with metadata like author names and
-    log entries.</li>
-
-    <li><a name="view-log" href="help_log.html"><strong>Log
-    View</strong></a> - Shows a revision by revision list of all the
-    changes that have made to a file or directory in the repository, with
-    metadata and links to views of each revision.</li>
-
-    <li><a name="view-markup"><strong>File Contents View (Markup
-    View)</strong></a> - Shows the contents of a file at a particular
-    revision, with revision information at the top of the page. File
-    revisions which are GIF, PNG, or JPEG images are displayed inline on
-    the page. Other file types are displayed as marked up text. The markup
-    may be limited to turning URLs and email addresses into links, or
-    configured to show colorized source code.</li>
-
-    <li><a name="view-checkout"><strong>File Download (Checkout
-    View)</strong></a> - Retrieves the unaltered contents of a file
-    revision. Browsers may try to display the file, or just save it to
-    disk.</li>
-
-    <li><a name="view-annotate"><strong>File Annotate View</strong></a> -
-    Shows the contents of a file revision and breaks it down line by line,
-    showing the revision number where each one was last modified, along
-    with links and other information. <em>This view is disabled in some
-    ViewVC configurations</em></li>
-
-    <li><a name="view-diff"><strong>File Diff View</strong></a> - Shows
-    the changes made between two revisions of a file</li>
-
-    <li><a name="view-tarball"><strong>Directory Tarball View</strong> -
-    Retrieves a gzipped tar archive containing the contents of a
-    directory.<em>This view is disabled in the default ViewVC
-    configuration.</em></li>
-
-    <li><a name="view-query"><strong>Directory Query View</strong></a> -
-    Shows information about changes made to all subdirectories and files
-    under a parent directory, sorted and filtered by criteria you specify.
-    <em>This view is disabled in the default ViewVC configuration.</em>
-    </li>
-
-    <li><a name="view-rev"><strong>Revision View</strong> - Shows
-    information about a revision including log message, author, and a list
-    of changed paths. <em>For Subversion repositories only.</em></li>
-
-    <li><a name="view-graph"><strong>Graph View</strong></a> - Shows a
-    graphical representation of a file's revisions and branches complete
-    with tag and author names and links to markup and diff pages.
-    <em>For CVS repositories only, and disabled in the default
-    configuration.</em></li>
-  </ul>
-
-  <h3><a name="multiple-repositories">Multiple Repositories</a></h3>
-
-  <p>A single installation of ViewVC is often used to provide access to
-  more than one repository. In these installations, ViewVC shows a
-  <em>Project Root</em> drop down box in the top right corner of every
-  generated page to allow for quick access to any repository.</p>
-
-  <h3><a name="sticky-revision-tag">Sticky Revision and Tag</a></h3>
-
-  <p>By default, ViewVC will show the files and directories and revisions
-  that currently exist in the repository. But it's also possible to browse
-  the contents of a repository at a point in its past history by choosing
-  a "sticky tag" (in CVS) or a "sticky revision" (in Subversion) from the
-  forms at the top of directory and log pages. They're called sticky
-  because once they're chosen, they stick around when you navigate to
-  other pages, until you reset them. When they're set, directory and log
-  pages only show revisions preceding the specified point in history. In
-  CVS, when a tag refers to a branch or a revision on a branch, only
-  revisions from the branch history are shown, including branch points and
-  their preceding revisions.</p>
-
-  <h3><a name="dead-files">Dead Files</a></h3>
-
-  <p>In CVS directory listings, ViewVC can optionally display dead files.
-  Dead files are files which used to be in a directory but are currently
-  deleted, or files which just don't exist in the currently selected
-  <a href="#sticky-revision-tag">sticky tag</a>. Dead files cannot be
-  shown in Subversion repositories. The only way to see a deleted file in
-  a Subversion directory is to navigate to a sticky revision where the
-  file previously existed.</p>
-
-  <h3><a name="artificial-tags">Artificial Tags</a></h3>
-
-  <p>In CVS Repositories, ViewVC adds artificial tags <em>HEAD</em> and
-  <em>MAIN</em> to tag listings and accepts them in place of revision
-  numbers and real tag names in all URLs. <em>MAIN</em> acts like a branch
-  tag pointing at the default branch, while <em>HEAD</em> acts like a
-  revision tag pointing to the latest revision on the default branch. The
-  default branch is usually just the trunk, but may be set to other
-  branches inside individual repository files. CVS will always check out
-  revisions from a file's default branch when no other branch is specified
-  on the command line.</p>
-
-  <h3><a name="more-information">More Information</a></h3>
-
-  <p>More information about <em>ViewVC</em> is available from
-  <a href="http://viewvc.org/";>viewvc.org</a>.
-  See the links below for guides to CVS and Subversion</p>
-
-  <h4>Documentation about CVS</h4>
-  <blockquote>
-    <p>
-      <a href="http://cvsbook.red-bean.com/";><em>Open Source
-      Development with CVS</em></a><br />
-      <a href="http://www.loria.fr/~molli/cvs/doc/cvs_toc.html";>CVS
-      User's Guide</a><br />
-      <a href="http://cellworks.washington.edu/pub/docs/cvs/tutorial/cvs_tutorial_1.html";>Another CVS tutorial</a><br />
-      <a href="http://www.csc.calpoly.edu/~dbutler/tutorials/winter96/cvs/";>Yet another CVS tutorial (a little old, but nice)</a><br />
-      <a href="http://www.cs.utah.edu/dept/old/texinfo/cvs/FAQ.txt";>An old but very useful FAQ about CVS</a>
-    </p>
-  </blockquote>
-
-  <h4>Documentation about Subversion</h3>
-  <blockquote>
-    <p>
-      <a href="http://svnbook.red-bean.com/";><em>Version Control with
-      Subversion</em></a><br />
-    </p>
-  </blockquote>
+    <h1>ViewVC Help: General</h1>
 
+    <p><em>ViewVC</em> is a WWW interface for CVS and Subversion
+    repositories. It allows you to browse the files and directories in a
+    repository while showing you metadata from the repository history: log
+    messages, modification dates, author names, revision numbers, copy
+    history, and so on. It provides several different views of repository
+    data to help you find the information you are looking for:</p>
+  
+    <ul>
+      <li><a name="multiple-repositories"><strong>Root Listing
+      View</strong></a> - Show a list of repositories configured for
+      display in ViewVC.</li>
+  
+      <li><a name="view-dir" href="help_dirview.html"><strong>Directory
+      View</strong></a> - Shows a list of files and subdirectories in a
+      directory of the repository, along with metadata like author names and
+      log entries.</li>
+  
+      <li><a name="view-log" href="help_log.html"><strong>Log
+      View</strong></a> - Shows a revision by revision list of all the
+      changes that have made to a file or directory in the repository, with
+      metadata and links to views of each revision.</li>
+  
+      <li><a name="view-checkout"><strong>File Download (Checkout
+      View)</strong></a> - Retrieves the unaltered contents of a file
+      revision. Browsers may try to display the file, or just save it
+      to disk.  <em>This view is disabled in the default ViewVC
+      configuration.</em></li>
+  
+      <li><a name="view-annotate"><a name="view-markup"><strong>File
+      Contents View</strong></a></a> - Shows the contents of a file at
+      a particular revision, with revision information at the top of
+      the page. File revisions which are GIF, PNG, or JPEG images are
+      displayed inline on the page. Other file types are displayed as
+      marked up text. The markup may be limited to turning URLs and
+      email addresses into links, or configured to show colorized
+      source code.  This view can optionally show line-based
+      annotation data for the file, containing the revision number
+      where each line was last modified, along with links and other
+      information.  <em>This view is disabled in some ViewVC
+      configurations.</em></li>
+  
+      <li><a name="view-diff"><strong>File Diff View</strong></a> - Shows
+      the changes made between two revisions of a file</li>
+  
+      <li><a name="view-tarball"><strong>Directory Tarball View</strong> -
+      Retrieves a gzipped tar archive containing the contents of a
+      directory.<em>This view is disabled in the default ViewVC
+      configuration.</em></li>
+  
+      <li><a name="view-query"><strong>Directory Query View</strong></a> -
+      Shows information about changes made to all subdirectories and files
+      under a parent directory, sorted and filtered by criteria you specify.
+      <em>This view is disabled in the default ViewVC configuration.</em>
+      </li>
+  
+      <li><a name="view-rev"><strong>Revision View</strong> - Shows
+      information about a revision including log message, author, and a list
+      of changed paths. <em>For Subversion repositories only.</em></li>
+  
+      <li><a name="view-graph"><strong>Graph View</strong></a> - Shows a
+      graphical representation of a file's revisions and branches complete
+      with tag and author names and links to markup and diff pages.
+      <em>For CVS repositories only, and disabled in the default
+      configuration.</em></li>
+    </ul>
+  
+    <h3><a name="sticky-revision-tag">Sticky Revision and Tag</a></h3>
+  
+    <p>By default, ViewVC will show the files and directories and revisions
+    that currently exist in the repository. But it's also possible to browse
+    the contents of a repository at a point in its past history by choosing
+    a "sticky tag" (in CVS) or a "sticky revision" (in Subversion) from the
+    forms at the top of directory and log pages. They're called sticky
+    because once they're chosen, they stick around when you navigate to
+    other pages, until you reset them. When they're set, directory and log
+    pages only show revisions preceding the specified point in history. In
+    CVS, when a tag refers to a branch or a revision on a branch, only
+    revisions from the branch history are shown, including branch points and
+    their preceding revisions.</p>
+  
+    <h3><a name="dead-files">Dead Files</a></h3>
+  
+    <p>In CVS directory listings, ViewVC can optionally display dead files.
+    Dead files are files which used to be in a directory but are currently
+    deleted, or files which just don't exist in the currently selected
+    <a href="#sticky-revision-tag">sticky tag</a>. Dead files cannot be
+    shown in Subversion repositories. The only way to see a deleted file in
+    a Subversion directory is to navigate to a sticky revision where the
+    file previously existed.</p>
+  
+    <h3><a name="artificial-tags">Artificial Tags</a></h3>
+  
+    <p>In CVS Repositories, ViewVC adds artificial tags <em>HEAD</em> and
+    <em>MAIN</em> to tag listings and accepts them in place of revision
+    numbers and real tag names in all URLs. <em>MAIN</em> acts like a branch
+    tag pointing at the default branch, while <em>HEAD</em> acts like a
+    revision tag pointing to the latest revision on the default branch. The
+    default branch is usually just the trunk, but may be set to other
+    branches inside individual repository files. CVS will always check out
+    revisions from a file's default branch when no other branch is specified
+    on the command line.</p>
+  
+    <h3><a name="more-information">More Information</a></h3>
+  
+    <p>More information about <em>ViewVC</em> is available from
+    <a href="http://viewvc.org/";>viewvc.org</a>.
+    See the links below for guides to CVS and Subversion</p>
+  
+    <h4>Documentation about CVS</h4>
+    <blockquote>
+      <p>
+        <a href="http://cvsbook.red-bean.com/";><em>Open Source
+        Development with CVS</em></a><br />
+        <a href="http://www.loria.fr/~molli/cvs/doc/cvs_toc.html";>CVS
+        User's Guide</a><br />
+        <a href="http://cellworks.washington.edu/pub/docs/cvs/tutorial/cvs_tutorial_1.html";>Another CVS tutorial</a><br />
+        <a href="http://www.csc.calpoly.edu/~dbutler/tutorials/winter96/cvs/";>Yet another CVS tutorial (a little old, but nice)</a><br />
+        <a href="http://www.cs.utah.edu/dept/old/texinfo/cvs/FAQ.txt";>An old but very useful FAQ about CVS</a>
+      </p>
+    </blockquote>
+  
+    <h4>Documentation about Subversion</h3>
+    <blockquote>
+      <p>
+        <a href="http://svnbook.red-bean.com/";><em>Version Control with
+        Subversion</em></a><br />
+      </p>
+    </blockquote>
   </td></tr></table>
   <hr />
   <address><a href="mailto:users viewvc tigris org">ViewVC Users Mailinglist</a></address>

Modified: trunk/templates/docroot/styles.css
==============================================================================
--- trunk/templates/docroot/styles.css	(original)
+++ trunk/templates/docroot/styles.css	Mon Nov  3 09:57:02 2008
@@ -6,6 +6,7 @@
 #body {
   margin: 90px 10px 0px 10px;
   padding: 0px;
+  font-family: sans-serif;
 }
 
 a:link    { color: #0000ff; }
@@ -31,8 +32,10 @@
   white-space: nowrap;
 }
 tr, td, th { vertical-align: top; }
+th { white-space: nowrap; }
 form { margin: 0; }
 
+
 /*** Icons ***/
 .vc_icon {
   width: 16px;
@@ -48,6 +51,10 @@
   margin: 0.25em;
 	-moz-border-radius: 15px;
 	background: #dddddd;
+  padding: .25em;
+}
+.vc_navheader .pathdiv {
+  padding: 0 3px;
 }
 
 
@@ -68,7 +75,7 @@
   background-color: #ffffff;
 }
 .vc_row_odd {
-  background-color: #eeeeee;
+  background-color: #f0f0f0;
 }
 .vc_row_special {
   background-color: #ffff7f;
@@ -86,52 +93,108 @@
 }
 
 
-/*** Markup Summary Header ***/
+/*** Properties Listing ***/
+.vc_properties {
+  margin: 1em 0;
+}
+.vc_properties h2 {
+  font-size: 115%;
+}
+
+
+/*** File Content Markup Styles ***/
 .vc_summary {
   background-color: #ececec;
   border: 1px solid #807d74;
   padding: 2px;
   margin: 0.25em;
 }
-
-#vc_markup {
-	font-family: monospace;
-}
-
-/*** Highlight Markup Styles ***/
-#vc_markup .num  { color: #000000; }
-#vc_markup .esc  { color: #bd8d8b; }
-#vc_markup .str  { color: #bd8d8b; }
-#vc_markup .dstr { color: #bd8d8b; }
-#vc_markup .slc  { color: #ac2020; font-style: italic; }
-#vc_markup .com  { color: #ac2020; font-style: italic; }
-#vc_markup .dir  { color: #000000; }
-#vc_markup .sym  { color: #000000; }
-#vc_markup .line { color: #555555; }
-#vc_markup .kwa  { color: #9c20ee; font-weight: bold; }
-#vc_markup .kwb  { color: #208920; }
-#vc_markup .kwc  { color: #0000ff; }
-#vc_markup .kwd  { color: #404040; }
-
-/*** Py2html Markup Styles  ***/
-#vc_markup .PY_STRING     { color: #bd8d8b; }
-#vc_markup .PY_COMMENT    { color: #ac2020; font-style: italic; }
-#vc_markup .PY_KEYWORD    { color: #9c20ee; font-weight: bold; }
-#vc_markup .PY_IDENTIFIER { color: #404040; }
-
-/*** Line numbers outputted by highlight colorizer ***/
-.line {
-  border-right-width: 1px;
+#vc_file td {
   border-right-style: solid;
   border-right-color: #505050;
-  padding: 1px;
-  background-color: #eeeeee;
-  color: #505050;
   text-decoration: none;
   font-weight: normal;
   font-style: normal;
+  padding: 1px 5px;
+}
+.vc_file_line_number {
+  border-right-width: 1px;
+  background-color: #eeeeee;
+  color: #505050;
   text-align: right;
 }
+.vc_file_line_author, .vc_file_line_rev {
+  border-right-width: 1px;
+  text-align: right;
+}
+.vc_file_line_text {
+  border-right-width: 0px;
+  background-color: white;
+  font-family: monospace;
+  text-align: left;
+  white-space: pre;
+  width: 100%;
+}
+.pygments-c { color: #408080; font-style: italic } /* Comment */
+.pygments-err { border: 1px solid #FF0000 } /* Error */
+.pygments-k { color: #008000; font-weight: bold } /* Keyword */
+.pygments-o { color: #666666 } /* Operator */
+.pygments-cm { color: #408080; font-style: italic } /* Comment.Multiline */
+.pygments-cp { color: #BC7A00 } /* Comment.Preproc */
+.pygments-c1 { color: #408080; font-style: italic } /* Comment.Single */
+.pygments-cs { color: #408080; font-style: italic } /* Comment.Special */
+.pygments-gd { color: #A00000 } /* Generic.Deleted */
+.pygments-ge { font-style: italic } /* Generic.Emph */
+.pygments-gr { color: #FF0000 } /* Generic.Error */
+.pygments-gh { color: #000080; font-weight: bold } /* Generic.Heading */
+.pygments-gi { color: #00A000 } /* Generic.Inserted */
+.pygments-go { color: #808080 } /* Generic.Output */
+.pygments-gp { color: #000080; font-weight: bold } /* Generic.Prompt */
+.pygments-gs { font-weight: bold } /* Generic.Strong */
+.pygments-gu { color: #800080; font-weight: bold } /* Generic.Subheading */
+.pygments-gt { color: #0040D0 } /* Generic.Traceback */
+.pygments-kc { color: #008000; font-weight: bold } /* Keyword.Constant */
+.pygments-kd { color: #008000; font-weight: bold } /* Keyword.Declaration */
+.pygments-kp { color: #008000 } /* Keyword.Pseudo */
+.pygments-kr { color: #008000; font-weight: bold } /* Keyword.Reserved */
+.pygments-kt { color: #B00040 } /* Keyword.Type */
+.pygments-m { color: #666666 } /* Literal.Number */
+.pygments-s { color: #BA2121 } /* Literal.String */
+.pygments-na { color: #7D9029 } /* Name.Attribute */
+.pygments-nb { color: #008000 } /* Name.Builtin */
+.pygments-nc { color: #0000FF; font-weight: bold } /* Name.Class */
+.pygments-no { color: #880000 } /* Name.Constant */
+.pygments-nd { color: #AA22FF } /* Name.Decorator */
+.pygments-ni { color: #999999; font-weight: bold } /* Name.Entity */
+.pygments-ne { color: #D2413A; font-weight: bold } /* Name.Exception */
+.pygments-nf { color: #0000FF } /* Name.Function */
+.pygments-nl { color: #A0A000 } /* Name.Label */
+.pygments-nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
+.pygments-nt { color: #008000; font-weight: bold } /* Name.Tag */
+.pygments-nv { color: #19177C } /* Name.Variable */
+.pygments-ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
+.pygments-w { color: #bbbbbb } /* Text.Whitespace */
+.pygments-mf { color: #666666 } /* Literal.Number.Float */
+.pygments-mh { color: #666666 } /* Literal.Number.Hex */
+.pygments-mi { color: #666666 } /* Literal.Number.Integer */
+.pygments-mo { color: #666666 } /* Literal.Number.Oct */
+.pygments-sb { color: #BA2121 } /* Literal.String.Backtick */
+.pygments-sc { color: #BA2121 } /* Literal.String.Char */
+.pygments-sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */
+.pygments-s2 { color: #BA2121 } /* Literal.String.Double */
+.pygments-se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
+.pygments-sh { color: #BA2121 } /* Literal.String.Heredoc */
+.pygments-si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
+.pygments-sx { color: #008000 } /* Literal.String.Other */
+.pygments-sr { color: #BB6688 } /* Literal.String.Regex */
+.pygments-s1 { color: #BA2121 } /* Literal.String.Single */
+.pygments-ss { color: #19177C } /* Literal.String.Symbol */
+.pygments-bp { color: #008000 } /* Name.Builtin.Pseudo */
+.pygments-vc { color: #19177C } /* Name.Variable.Class */
+.pygments-vg { color: #19177C } /* Name.Variable.Global */
+.pygments-vi { color: #19177C } /* Name.Variable.Instance */
+.pygments-il { color: #666666 } /* Literal.Number.Integer.Long */
+
 
 /*** Diff Styles ***/
 .vc_diff_header {
@@ -171,8 +234,8 @@
   font-family: monospace;
 }
 
-/*** Intraline Diff Styles ***/
 
+/*** Intraline Diff Styles ***/
 .vc_idiff_add {
   background-color: #aaffaa;
 }
@@ -185,7 +248,6 @@
 .vc_idiff_empty {
   background-color:#e0e0e0;
 }
-
 table.vc_idiff col.content { 
   width: 50%;
 }
@@ -203,39 +265,6 @@
   text-align:right;
 }
 
-/*** Annotate Styles ***/
-div.vc_blame {
-	margin: 1em 0;
-}
-
-#vc_blame td {
-  border-right-width: 1px;
-  border-right-style: solid;
-  border-right-color: #505050;
-  text-decoration: none;
-  font-weight: normal;
-  font-style: normal;
-}
-.vc_blame_line {
-  background-color: #eeeeee;
-  color: #505050;
-  padding: 1px 1px;
-  font-size: 80%;
-}
-.vc_blame_author, .vc_blame_rev, .vc_blame_line {
-  text-align: right;
-  font-size: smaller;
-}
-.vc_blame_rev, .vc_blame_line {
-  padding: 1px 5px;
-}
-.vc_blame_text {
-  font-family: monospace;
-  text-align: left;
-  white-space: pre;
-  width: 100%;
-}
-
 /*** Query Form ***/
 .vc_query_form {
   background-color: #e6e6e6;

Modified: trunk/templates/include/diff_form.ezt
==============================================================================
--- trunk/templates/include/diff_form.ezt	(original)
+++ trunk/templates/include/diff_form.ezt	Mon Nov  3 09:57:02 2008
@@ -15,7 +15,7 @@
   <tr>
   <td>&nbsp;</td>
   <td>
-  [diff_select_hidden_values]
+  [for diff_select_hidden_values]<input type="hidden" name="[diff_select_hidden_values.name]" value="[diff_select_hidden_values.value]"/>[end]
   Diffs between
 [if-any tags]
   <select name="r1">

Modified: trunk/templates/include/dir_footer.ezt
==============================================================================
--- trunk/templates/include/dir_footer.ezt	(original)
+++ trunk/templates/include/dir_footer.ezt	Mon Nov  3 09:57:02 2008
@@ -1,38 +1,9 @@
-[if-any search_re_form]
-  <hr />
-  [# this table holds the selectors on the left, and reset on the right ]
-  <table class="auto">
-        <tr>
-          <td>Show files containing the regular expression:</td>
-          <td>
-            <form method="get" action="[search_re_action]">
-              <div>
-                [search_re_hidden_values]
-                <input type="text" name="search" value="[search_re]" />
-                <input type="submit" value="Show" />
-              </div>
-            </form>
-          </td>
-        </tr>
-      [if-any search_re]
-        <tr>
-          <td>&nbsp;</td>
-          <td>
-            <form method="get" action="[search_tag_action]">
-              <div>
-                [search_tag_hidden_values]
-                <input type="submit" value="Show all files" />
-              </div>
-            </form>
-          </td>
-        </tr>
-    [end]
-  </table>
-[end]
-
 [# if you want to disable tarball generation remove the following: ]
 [if-any tarball_href]
-  <p style="margin:0;"><a href="[tarball_href]">Download GNU tarball</a></p>
+<hr/>
+<p style="margin:0;"><a href="[tarball_href]">Download GNU tarball</a></p>
 [end]
 
+[include "props.ezt"]
 [include "footer.ezt"]
+

Modified: trunk/templates/include/dir_header.ezt
==============================================================================
--- trunk/templates/include/dir_header.ezt	(original)
+++ trunk/templates/include/dir_header.ezt	Mon Nov  3 09:57:02 2008
@@ -31,8 +31,27 @@
   <td>Sticky [is roottype "cvs"]Tag[else]Revision[end]:</td>
   <td>[include "pathrev_form.ezt"]</td>
 </tr>
-[if-any search_re]
-<tr><td>Current search:</td><td><strong>[search_re]</strong></td></tr>
+
+[if-any search_re_form]
+<tr>
+  <td>Filter files by content:</td>
+  <td><form method="get" action="[search_re_action]" style="display: inline;">
+      <div style="display: inline;">
+      [for search_re_hidden_values]<input type="hidden" name="[search_re_hidden_values.name]" value="[search_re_hidden_values.value]"/>[end]
+      <input type="text" name="search" value="[search_re]" />
+      <input type="submit" value="Search Regexp" />
+      </div>
+     </form>
+     [if-any search_re]
+     <form method="get" action="[search_re_action]" style="display: inline;">
+     <div style="display: inline;">
+     [for search_re_hidden_values]<input type="hidden" name="[search_re_hidden_values.name]" value="[search_re_hidden_values.value]"/>[end]
+     <input type="submit" value="Show All Files" />
+     </div>
+     </form>
+     [end]
+  </td>
+</tr>
 [end]
 
 [if-any queryform_href]
@@ -41,24 +60,25 @@
   <td><a href="[queryform_href]">Query revision history</a></td>
 </tr>
 [end]
+
 </table>
-  [is cfg.options.use_pagesize "0"]
+
+[is cfg.options.use_pagesize "0"]
+[else]
+  [is picklist_len "1"]
   [else]
-    [is picklist_len "1"]
-    [else]
-      <form method="get" action="[dir_paging_action]">
-        [dir_paging_hidden_values]
-        <input type="submit" value="Go to:" />
-        <select name="dir_pagestart"  onchange="submit()">
-          [for picklist]
-            <option [is picklist.count dir_pagestart]selected[end] value="[picklist.count]">Page [picklist.page]: [picklist.start] to [picklist.end]</option>
-          [end]
-        </select>
-      </form>
-    [end]
+    <form method="get" action="[dir_paging_action]">
+      [for dir_paging_hidden_values]<input type="hidden" name="[dir_paging_hidden_values.name]" value="[dir_paging_hidden_values.value]"/>[end]
+      <input type="submit" value="Go to:" />
+      <select name="dir_pagestart"  onchange="submit()">
+        [for picklist]
+          <option [is picklist.count dir_pagestart]selected[end] value="[picklist.count]">Page [picklist.page]: [picklist.start] to [picklist.end]</option>
+        [end]
+      </select>
+    </form>
   [end]
+[end]
 
 <p><a name="dirlist"></a></p>
-
 <hr />
 

Modified: trunk/templates/include/header.ezt
==============================================================================
--- trunk/templates/include/header.ezt	(original)
+++ trunk/templates/include/header.ezt	Mon Nov  3 09:57:02 2008
@@ -59,63 +59,10 @@
     <div id="content">
 
 <div class="vc_navheader">
-[if-any roots]
-  <form method="get" action="[change_root_action]">
-[end]
-<table style="padding:0.1em;">
-<tr>
-  <td>
-  [if-any nav_path]<strong>
-    [for nav_path]
-      [if-any nav_path.href]<a href="[nav_path.href]">[end]
-      [if-index nav_path first]
-        [[][nav_path.name]][else]
-        [nav_path.name][end][if-any nav_path.href]</a>[end]
-      [if-index nav_path last][else]/[end]
-    [end]
-    </strong>
-  [end]
-  </td>
-  <td style="text-align:right;">
-  [if-any roots]
-    [change_root_hidden_values]
-      <strong>Repository:</strong>
-      <select name="root" onchange="submit()">
-        [define cvs_root_options][end]
-        [define svn_root_options][end]
-        <option value="*viewroots*"[is view "roots"] selected="selected"[else][end]>Repository Listing</option>
-        [for roots]
-          [define root_option][end]
-          [is roots.name rootname]
-            [define root_option]<option selected="selected">[roots.name]</option>[end]
-          [else]
-            [define root_option]<option>[roots.name]</option>[end]
-          [end]
-          [is roots.type "cvs"]
-            [define cvs_root_options][cvs_root_options][root_option][end]
-          [else]
-            [is roots.type "svn"]
-              [define svn_root_options][svn_root_options][root_option][end]
-            [end]
-          [end]
-        [end]
-        [is cvs_root_options ""][else]
-          <optgroup label="CVS Repositories">[cvs_root_options]</optgroup>
-        [end]
-        [is svn_root_options ""][else]
-          <optgroup label="Subversion Repositories">[svn_root_options]</optgroup>
-        [end]
-      </select>
-      <input type="submit" value="Go" />
-  [else]
-    &nbsp;
-  [end]
-  </td>
-</tr>
-</table>
-[if-any roots]
-</form>
-[end]
+<table><tr>
+<td><strong>[if-any roots_href]<a href="[roots_href]"><span class="pathdiv">/</span></a>[else]<span class="pathdiv">/</span>[end][if-any nav_path][for nav_path][if-any nav_path.href]<a href="[nav_path.href]">[end][if-index nav_path first][[][nav_path.name]][else][nav_path.name][end][if-any nav_path.href]</a>[end][if-index nav_path last][else]<span class="pathdiv">/</span>[end][end][end]</strong></td>
+<td style="text-align: right;">[if-any username]Logged in as: <strong>[username]</strong>[end]</td>
+</tr></table>
 </div>
 
 

Modified: trunk/templates/include/log_header.ezt
==============================================================================
--- trunk/templates/include/log_header.ezt	(original)
+++ trunk/templates/include/log_header.ezt	Mon Nov  3 09:57:02 2008
@@ -19,14 +19,14 @@
 [end]
 
 [is pathtype "file"]
-[if-any view_href]
+[if-any head_view_href]
 <tr>
   <td>Links to HEAD:</td>
   <td>
-    (<a href="[view_href]">view</a>)
-    [if-any download_href](<a href="[download_href]">download</a>)[end]
-    [if-any download_text_href](<a href="[download_text_href]">as text</a>)[end]
-    [if-any annotate_href](<a href="[annotate_href]">annotate</a>)[end]
+    (<a href="[head_view_href]">view</a>)
+    [if-any head_download_href](<a href="[head_download_href]">download</a>)[end]
+    [if-any head_download_text_href](<a href="[head_download_text_href]">as text</a>)[end]
+    [if-any head_annotate_href](<a href="[head_annotate_href]">annotate</a>)[end]
   </td>
 </tr>
 [end]

Modified: trunk/templates/include/paging.ezt
==============================================================================
--- trunk/templates/include/paging.ezt	(original)
+++ trunk/templates/include/paging.ezt	Mon Nov  3 09:57:02 2008
@@ -4,11 +4,15 @@
     [else]
       <hr />
       <form method="get" action="[log_paging_action]">
-        [log_paging_hidden_values]
+        [for log_paging_hidden_values]<input type="hidden" name="[log_paging_hidden_values.name]" value="[log_paging_hidden_values.value]"/>[end]
         <input type="submit" value="Go to:">
         <select name="log_pagestart"  onchange="submit()">
           [for picklist]
+           [if-any picklist.more]
+            <option [is picklist.count log_pagestart]selected[end] value="[picklist.count]">Page [picklist.page]: [picklist.start] ...</option>
+           [else]
             <option [is picklist.count log_pagestart]selected[end] value="[picklist.count]">Page [picklist.page]: [picklist.start] - [picklist.end]</option>
+           [end]
           [end]
         </select>
       </form>

Modified: trunk/templates/include/pathrev_form.ezt
==============================================================================
--- trunk/templates/include/pathrev_form.ezt	(original)
+++ trunk/templates/include/pathrev_form.ezt	Mon Nov  3 09:57:02 2008
@@ -1,6 +1,6 @@
 <form method="get" action="[pathrev_action]" style="display: inline">
 <div style="display: inline">
-[pathrev_hidden_values]
+[for pathrev_hidden_values]<input type="hidden" name="[pathrev_hidden_values.name]" value="[pathrev_hidden_values.value]"/>[end]
 [is roottype "cvs"]
   [define pathrev_selected][pathrev][end]
   <select name="pathrev" onchange="submit()">
@@ -41,7 +41,7 @@
 [if-any pathrev]
 <form method="get" action="[pathrev_clear_action]" style="display: inline">
 <div style="display: inline">
-[pathrev_clear_hidden_values]
+[for pathrev_clear_hidden_values]<input type="hidden" name="[pathrev_clear_hidden_values.name]" value="[pathrev_clear_hidden_values.value]"/>[end]
 [if-any lastrev]
   [is pathrev lastrev][else]<input type="submit" value="Set to [lastrev]" />[end]
   (<i>Current path doesn't exist after revision <strong>[lastrev]</strong></i>)

Modified: trunk/templates/include/sort.ezt
==============================================================================
--- trunk/templates/include/sort.ezt	(original)
+++ trunk/templates/include/sort.ezt	Mon Nov  3 09:57:02 2008
@@ -1,8 +1,10 @@
+[is roottype "svn"]
+[else]
 <form method="get" action="[logsort_action]">
   <div>
     <hr />
     <a name="logsort"></a>
-      [logsort_hidden_values]
+      [for logsort_hidden_values]<input type="hidden" name="[logsort_hidden_values.name]" value="[logsort_hidden_values.value]"/>[end]
       Sort log by:
       <select name="logsort" onchange="submit()">
         <option value="cvs" [is logsort "cvs"]selected="selected"[end]>Not sorted</option>
@@ -12,3 +14,4 @@
       <input type="submit" value="  Sort  " />
   </div>
 </form>
+[end]
\ No newline at end of file

Modified: trunk/templates/log.ezt
==============================================================================
--- trunk/templates/log.ezt	(original)
+++ trunk/templates/log.ezt	Mon Nov  3 09:57:02 2008
@@ -6,6 +6,7 @@
 [for entries]
 [if-index entries first][define first_revision][entries.rev][end][end]
 [if-index entries last][define last_revision][entries.rev][end][end]
+
 <div>
   <hr />
 
@@ -97,6 +98,10 @@
     [end]
   [end]
 
+  [if-any entries.lockinfo]
+    <br />Lock status: <img src="[docroot]/images/lock.png" alt="Locked" width="16" height="16" /> [entries.lockinfo]
+  [end]
+
   [is entries.state "dead"]
     <br /><strong><em>FILE REMOVED</em></strong>
   [else]

Modified: trunk/templates/log_table.ezt
==============================================================================
--- trunk/templates/log_table.ezt	(original)
+++ trunk/templates/log_table.ezt	Mon Nov  3 09:57:02 2008
@@ -110,7 +110,7 @@
       [# Tags ]
       [if-any entries.tags]
         <form method=get action="[pathrev_action]" >
-          [pathrev_hidden_values]
+          [for pathrev_hidden_values]<input type="hidden" name="[pathrev_hidden_values.name]" value="[pathrev_hidden_values.value]"/>[end]
           <select name="pathrev" onChange="submit()">
           <option value="" [is pathrev ""]selected[end]>Show all tags</option>
           [for entries.tags]
@@ -147,9 +147,14 @@
   </tr>
   <tr class="vc_row_[if-index entries even]even[else]odd[end]">
     <td colspan=5>
+
+      [if-any entries.lockinfo]
+        <strong>Lock status</strong>: <img src="[docroot]/images/lock.png" alt="Locked" width="16" height="16" /> [entries.lockinfo]<br />
+      [end]
+
       [is roottype "svn"]
         [if-any entries.orig_path]
-          Original Path: <a href="[entries.orig_href]"><em>[entries.orig_path]</em></a><br />
+          <strong>Original Path</strong>: <a href="[entries.orig_href]"><em>[entries.orig_path]</em></a><br />
         [end]
 
         [if-any entries.size]

Modified: trunk/templates/query_form.ezt
==============================================================================
--- trunk/templates/query_form.ezt	(original)
+++ trunk/templates/query_form.ezt	Mon Nov  3 09:57:02 2008
@@ -12,7 +12,7 @@
 <form action="[query_action]" method="get">
 
 <div class="vc_query_form">
-  [query_hidden_values]
+  [for query_hidden_values]<input type="hidden" name="[query_hidden_values.name]" value="[query_hidden_values.value]"/>[end]
 <table cellspacing="0" cellpadding="5" class="auto">
   [is roottype "cvs"]
   [# For subversion, the branch field is not used ]

Modified: trunk/templates/revision.ezt
==============================================================================
--- trunk/templates/revision.ezt	(original)
+++ trunk/templates/revision.ezt	Mon Nov  3 09:57:02 2008
@@ -11,7 +11,7 @@
   <tr align="left">
     <th>Jump to revision:</th>
     <td>
-      [jump_rev_hidden_values]
+      [for jump_rev_hidden_values]<input type="hidden" name="[jump_rev_hidden_values.name]" value="[jump_rev_hidden_values.value]"/>[end]
       <input type="text" name="revision" value="[rev]" />
       <input type="submit" value="Go" />
       [if-any prev_href]

Modified: trunk/tparse/tparse.h
==============================================================================
--- trunk/tparse/tparse.h	(original)
+++ trunk/tparse/tparse.h	Mon Nov  3 09:57:02 2008
@@ -1,5 +1,5 @@
 /*
-# Copyright (C) 1999-2006 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
 # the LICENSE.html file which can be found at the top level of the ViewVC

Modified: trunk/viewvc-install
==============================================================================
--- trunk/viewvc-install	(original)
+++ trunk/viewvc-install	Mon Nov  3 09:57:02 2008
@@ -49,8 +49,8 @@
 FILE_INFO_LIST = [
     ("bin/cgi/viewvc.cgi",        "bin/cgi/viewvc.cgi",        0755, 1, 0, 0),
     ("bin/cgi/query.cgi",         "bin/cgi/query.cgi",         0755, 1, 0, 0),
-    ("bin/mod_python/viewvc_mp.py",  "bin/mod_python/viewvc_mp.py", 0755, 1, 0, 0),
-    ("bin/mod_python/query_mp.py",   "bin/mod_python/query_mp.py",  0755, 1, 0, 0),
+    ("bin/mod_python/viewvc.py",  "bin/mod_python/viewvc.py",  0755, 1, 0, 0),
+    ("bin/mod_python/query.py",   "bin/mod_python/query.py",   0755, 1, 0, 0),
     ("bin/mod_python/handler.py", "bin/mod_python/handler.py", 0755, 1, 0, 0),
     ("bin/mod_python/.htaccess",  "bin/mod_python/.htaccess",  0755, 0, 0, 0),
     ("bin/standalone.py",         "bin/standalone.py",         0755, 1, 0, 0),

Modified: trunk/viewvc.conf.dist
==============================================================================
--- trunk/viewvc.conf.dist	(original)
+++ trunk/viewvc.conf.dist	Mon Nov  3 09:57:02 2008
@@ -68,9 +68,6 @@
 #       address
 #       forbidden
 #   
-#       use_enscript
-#       use_cvsgraph
-#   
 #    To optimize delivery of ViewVC static files:
 #   
 #       docroot
@@ -238,50 +235,16 @@
 
 # Subversion command-line client, used for viewing Subversion repositories
 svn =
-# svn_dir = /usr/bin/svn
+# svn = /usr/bin/svn
 
 # GNU diff, used for showing file version differences
 diff = 
 # diff = /usr/bin/diff
 
-# GNU Enscript, a syntax highlighting program (see options.use_enscript)
-enscript =
-# enscript = /usr/bin/enscript
-
-# Highlight, a syntax highlighting program (see options.use_highlight)
-highlight =
-# highlight = /usr/bin/highlight
-
-# GNU source-highlight, a syntax highlighting program (see 
-# options.use_source_highlight)
-source_highlight = 
-# source_highlight = /usr/bin/source-highlight
-
-# Marc-Andrew Lemburg's py2html, Python colorizer (see options.use_py2html)
-py2html_dir = .
-# py2html_dir = /usr/local/lib/python1.5/site-python
-
-# PHP, used to colorize PHP files (see options.use_php)
-# (This should be set to the path of a PHP CLI executable, not the path
-# to a CGI executable. If you use a CGI executable, you may see "no input file
-# specified" or "force-cgi-redirect" errors instead of colorized source. The
-# output of "php -v" tells you whether an given executable is CLI or CGI.)
-php = php
-# php = /usr/local/bin/php
-# php = C:\Program Files\php\cli\php.exe
-
 # CvsGraph, a graphical CVS version graph generator (see options.use_cvsgraph)
 cvsgraph =
 # cvsgraph = /usr/local/bin/cvsgraph
 
-# Gzip, file compression utility (see options.allow_tar)
-gzip =
-# gzip = /usr/bin/gzip
-
-# Sed, stream editor
-sed =
-# sed = /usr/bin/sed
-
 
 #---------------------------------------------------------------------------
 [options]
@@ -304,8 +267,8 @@
 # allowed_views: List the ViewVC views which are enabled.  Views not
 # in this comma-delited list will not be served (or, will return an
 # error on attempted access).
-# Possible values: "tar", "annotate", "co", "markup"
-allowed_views = markup, annotate
+# Possible values: "tar", "annotate", "co", "markup", "roots"
+allowed_views = markup, annotate, roots
 
 # authorizer: The name of the ViewVC authorizer plugin to use when
 # authorizing access to repository contents.  This value must be the
@@ -315,26 +278,45 @@
 # vcauth.GenericViewVCAuthorizer).  You can provide custom parameters
 # to the authorizer module by defining configuration sections named
 # authz-MODULENAME and adding the parameter keys and values there.
-# ViewVC provides the following modules:  "svnauthz", "forbidden"
+#
+# ViewVC provides the following modules:
+#   svnauthz    - based on Subversion authz files
+#   forbidden   - simple path glob matches against top-level root directories
+#   forbiddenre - root and path matches against regular expressions
+#
+# NOTE: Only one authorizer may be in use for a given ViewVC request.
+# It doesn't matter if you configure the parameters of multiple
+# authorizer plugins -- only the authorizer whose name is configured
+# here (or effectively configured here via vhost configuration) will
+# be activated.
 authorizer = forbidden
 
+# hide_cvsroot: Don't show the CVSROOT directory
+#   1      Hide CVSROOT directory
+#   0      Show CVSROOT directory
+# NOTE: Someday this option may be removed in favor of letting
+# individual authorizer plugin hide the CVSROOT.
+hide_cvsroot = 1
+
 # mangle_email_addresses: Mangle email addresses in marked-up output.
-#   0      Markup un-mangled email addresses as hyperlinks
-#   1      Mangle (and don't markup as hyperlinks) email addresses
+# There are various levels of mangling available:
+#   0 - No mangling; markup un-mangled email addresses as hyperlinks
+#   1 - Obfuscation (using entity encoding); no hyperlinking
+#   2 - Data-dropping address truncation; no hyperlinking
 # Note: this will not effect the display of versioned file contents, only
 # addresses that appear in version control metadata (e.g. log messages).
 mangle_email_addresses = 0
 
-# default_file_view: "log" or "co"
+# default_file_view: "log", "co", or "markup"
 # 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
 # ViewCVS URLs, but "co" has the advantage that it allows ViewVC to serve
 # static HTML pages directly from a repository with working links
 # to other repository files
-# Note: Changing this option may cause old ViewCVS URLs that referred
-# to log pages to load checkout pages instead.
-# Also note: If you choose the "co" view, be sure to enable it (via
-# the allowed_views option)
+# Note: Changing this option may break compatibility with existing
+# bookmarked URLs.
+# Also note: If you choose one of the "co" or "markup" views, be sure
+# to enable it (via the allowed_views option)
 default_file_view = log
 
 # http_expiration_time: Expiration time (in seconds) for cacheable
@@ -351,6 +333,12 @@
 #   0      Don't generate Etags
 generate_etags = 1
 
+# svn_config_dir: Path of the Subversion runtime configuration
+# directory ViewVC should consult for various things, including cached
+# remote authentication credentials.  If unset, Subversion will use
+# the default location(s) ($HOME/.subversion, etc.)
+svn_config_dir = 
+
 # use the rcsparse Python module to retrieve CVS repository
 # information instead of invoking rcs utilities [EXPERIMENTAL]
 use_rcsparse = 0
@@ -373,10 +361,17 @@
 #   0      Show the files which are inside the Attic subdir
 hide_attic = 1
 
+# hide_errorful_entries: Hide or show errorful directory entries
+# (perhaps due to not being readable, or some other rlog parsing
+# error, etc.)
+#   1      Hide errorful entries from the directory display
+#   0      Show errorful entries (with their errors) in the directory display
+hide_errorful_entries = 0
+
 # log_sort: Sort order for log messages
 #   date   Sort revisions by date
 #   rev    Sort revision by revision number
-#   cvs    Don't sort them. Same order as CVS/RCS shows them.
+#   none   Use the version control system's ordering
 log_sort = date
 
 # diff_format: Default diff format
@@ -385,16 +380,13 @@
 #   c      Context diff
 #   s      Side by side
 #   l      Long human readable (more context)
+#   f      Full human readable (entire file)
 diff_format = h
 
-# hide_cvsroot: Don't show the CVSROOT directory
-#   1      Hide CVSROOT directory
-#   0      Show CVSROOT directory
-hide_cvsroot = 1
-
-# set to 1 to make lines break at spaces,
-# set to 0 to make no-break lines,
-# set to a positive integer to make the lines cut at that length
+# hr_breakable: Diff view line breaks
+#   1     lines break at spaces
+#   0     no line breaking
+# Or, use a positive integer > 1 to cut lines after that many characters
 hr_breakable = 1
 
 # give out function names in human readable diffs
@@ -406,7 +398,7 @@
 # ignore whitespaces for human readable diffs
 # (indendation and stuff ..)
 # ( '-w' option to diff)
-hr_ignore_white = 1
+hr_ignore_white = 0
 
 # ignore diffs which are caused by
 # keyword-substitution like $Id - Stuff
@@ -415,13 +407,11 @@
 
 # Enable highlighting of intraline changes in human readable diffs
 # this feature is experimental and currently requires python 2.4
-# 
 hr_intraline = 0
 
 # allow compression with gzip of output if the Browser accepts it
-# (HTTP_ACCEPT_ENCODING=gzip)
-# [make sure to have gzip in the path]
-allow_compress = 1
+# (HTTP_ACCEPT_ENCODING contains "gzip")
+#allow_compress = 1
 
 # The directory which contains the EZT templates used by ViewVC to
 # customize the display of the various output views.  ViewVC looks in
@@ -433,6 +423,9 @@
 # If %lang% occurs in the pathname, then the selected language will be
 # substituted.
 #
+# See also the [templates] configuration section, where you can
+# override templates on a per-view basis.
+#
 template_dir = templates
 
 # Web path to a directory that contains ViewVC static files
@@ -443,17 +436,19 @@
 # specified by the "template_dir" option).
 #docroot = /docroot
 
-# Show last changelog message for sub directories
-# The current implementation makes many assumptions and may show the
-# incorrect file at some times. The main assumption is that the last
-# modified file has the newest filedate. But some CVS operations
+# Show last changelog message for CVS subdirectories
+# NOTE: The current implementation makes many assumptions and may show
+# the incorrect file at some times. The main assumption is that the
+# last modified file has the newest filedate. But some CVS operations
 # touches the file without even when a new version is not checked in,
 # and TAG based browsing essentially puts this out of order, unless
-# the last checkin was on the same tag as you are viewing.
-# Enable this if you like the feature, but don't rely on correct results.
+# the last checkin was on the same tag as you are viewing.  Enable
+# this if you like the feature, but don't rely on correct results.
+#
+# ** WARNING: Enabling this will currently leak unauthorized path names **
 show_subdir_lastmod = 0
 
-# show a portion of the most recent log entry in directory listings
+# Show the most recent log entry in directory listings.
 show_logs = 1
 
 # Show log when viewing file contents
@@ -466,51 +461,24 @@
 use_localtime = 0
 #use_localtime = 1
 
-# == Configuration defaults ==
-# Defaults for configuration variables that shouldn't need
-# to be configured..
+### CONFIGURATION DEFAULTS ###
+###
+### Defaults for configuration variables that shouldn't need
+### to be configured..
 
 # the length to which the most recent log entry should be truncated when
 # shown in the directory view
 short_log_len = 80
 
-# should we use Marc-Andrew Lemburg's py2html (and Just van Rossum's
-# PyFontify) to colorize Python files?
-use_py2html = 0
-
-# should we use 'enscript' for syntax coloring?
-use_enscript = 1
-
-# should we use 'highlight' for syntax coloring?
-# NOTE: use_enscript has to be 0 or enscript will be used instead
-use_highlight = 0
-
-# should we add line numbers?
-highlight_line_numbers = 1
+# should we colorize known file content syntaxes?  (requires Pygments module)
+enable_syntax_coloration = 1
 
-# convert tabs to ## spaces (use 0 for no conversion)
-highlight_convert_tabs = 2
-
-# should we use 'source-highlight' for syntax coloring?
-# NOTE: use_enscript and use_highlight have to be 0
-use_source_highlight = 0
-
-# should we add line numbers?
-source_highlight_line_numbers = 1
-
-# use php to colorize .php and .inc files?
-use_php = 0
-
-#
 # Use CvsGraph. See http://www.akhphd.au.dk/~bertho/cvsgraph/ for
 # documentation and download. 
-#
 use_cvsgraph = 0
-# use_cvsgraph = 1
+#use_cvsgraph = 1
 
-#
 # Location of the customized cvsgraph configuration file.  
-#
 cvsgraph_conf = cvsgraph.conf
 
 #
@@ -566,8 +534,9 @@
 # appropriate option below and specify the currect location of the EZT
 # template file you wish to use for that view.
 # 
-# Templates are specified relative to the directory where this config
-# file resides, but absolute paths may also be used as well.
+# Templates are specified relative to the configured template
+# directory (see the "template_dir" option), but absolute paths may
+# also be used as well.
 #
 # If %lang% occurs in the pathname, then the selected language will be
 # substituted.
@@ -576,22 +545,21 @@
 #       [general] section, and based on the request's Accept-Language
 #       header.
 #
-#annotate = templates/annotate.ezt
-#diff = templates/diff.ezt
-#directory = templates/directory.ezt
+#diff = diff.ezt
+#directory = directory.ezt
 ### an alternative directory view
-#directory = templates/dir_new.ezt   
-#error = templates/error.ezt
-#graph = templates/graph.ezt
-#log = templates/log.ezt
+#directory = dir_new.ezt   
+#error = error.ezt
+#file = file.ezt
+#graph = graph.ezt
+#log = log.ezt
 ### a table-based alternative log view
-#log = templates/log_table.ezt  
-#markup = templates/markup.ezt
-#query = templates/query.ezt
-#query_form = templates/query_form.ezt
-#query_results = templates/query_results.ezt
-#revision = templates/revision.ezt
-#roots = templates/roots.ezt
+#log = log_table.ezt  
+#query = query.ezt
+#query_form = query_form.ezt
+#query_results = query_results.ezt
+#revision = revision.ezt
+#roots = roots.ezt
 
 #---------------------------------------------------------------------------
 [cvsdb]
@@ -632,6 +600,17 @@
 # to a conservative number.)
 #rss_row_limit = 100
 
+# Check if the repository is found in the database before showing
+# the query link and RSS feeds.  Set to 1 to enable check.
+# 
+# WARNING: Enabling this check adds the cost of a database connection
+# and query to most ViewVC requests.  If all your roots are represented
+# in the commits database, or if you don't care about the creation of
+# RSS and query links that might lead ultimately to error pages for
+# certain of your roots, or if you simply don't want to add this extra
+# cost to your ViewVC requests, leave this disabled.
+#check_database_for_root = 0
+
 #---------------------------------------------------------------------------
 [vhosts]
 
@@ -699,6 +678,27 @@
 
 
 #---------------------------------------------------------------------------
+# ViewVC recognizes per-root configuration overrides, too.  To
+# override the value of a configuration parameter only for a single
+# root, create a configuration section whose names is of the form
+# root-ROOTNAME/CONFIGSECTION.  ROOTNAME here is the name of the root
+# as defined explicitly in cvs_roots or svn_roots or implicitly as the
+# basename of a root path in root_parents.  Options found in this new
+# configuration section override for this one root the corresponding
+# options found in the base configuration section CONFIGSECTION
+# ("options", "authz-*", etc.)
+#
+# Here is an example showing how to enable Subversion authz-based
+# authorization for only the single root named "svnroot":
+# 
+# [root-svnroot/options]
+# authorizer = svnauthz
+#
+# [root-svnroot/authz-svnauthz]
+# authzfile = /path/to/authzfile
+#
+
+#---------------------------------------------------------------------------
 [authz-forbidden]
 
 # The "forbidden" authorizer forbids access to repository modules,
@@ -745,6 +745,49 @@
 forbidden = 
 
 #---------------------------------------------------------------------------
+[authz-forbiddenre]
+
+# The "forbiddenre" authorizer forbids access to repositories and
+# repository paths by comparing a list of regular expressions
+# (separated by commas) against paths consisting of the repository (or
+# root) name plus the path of the versioned file or directory to be
+# tested.  For example, to see if the user is authorized to see the
+# path "/trunk/www/index.html" in the repository whose root name is
+# "svnrepos", this authorizer will check the path
+# "svnrepos/trunk/www/index.html" against the list of forbidden
+# regular expressions.  Directory paths will be terminated by a forward
+# slash.
+#
+# Like the "forbidden" authorizer...
+#
+#   *) The "!" can be used before a module to explicitly state that it
+#      is NOT forbidden. Whenever this form is seen, then all modules will
+#      be forbidden unless one of the "!" modules match.
+#
+#   *) Tests are performed in sequence. The first match will terminate the
+#      testing. This allows for more complex allow/deny patterns.
+#
+# Unlike the "forbidden" authorizer, you can can use this to hide roots, too.
+#
+# Some examples:
+#
+#    Disallow files named "PRIVATE", but allow all others:
+#       forbiddenre = /PRIVATE$
+#
+#    Disallow the "hidden" repository, allowing all others:
+#       forbiddenre = ^hidden(/|$)
+#
+#    Allow only the "example1" and "example2" roots and the paths inside them,
+#    disallowing all others (which can be done in multiple ways):
+#       forbiddenre = !^example1(/|$), !^example2(/|$)/
+#       forbiddenre = !^example[12](/|$)
+#
+#    Only allow visibility of HTML files and the directories that hold them:
+#       forbiddenre = !^([^/]+|.*(/|\.html))$
+#
+forbiddenre = 
+
+#---------------------------------------------------------------------------
 [authz-svnauthz]
 
 # The "svnauthz" authorizer uses a Subversion authz configuration file

Modified: trunk/viewvc.org/contact.html
==============================================================================
--- trunk/viewvc.org/contact.html	(original)
+++ trunk/viewvc.org/contact.html	Mon Nov  3 09:57:02 2008
@@ -1,10 +1,11 @@
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
                       "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";>
-<html>
+<html xmlns="http://www.w3.org/1999/xhtml";>
 <head>
 <title>ViewVC: Contact</title>
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
 <link rel="stylesheet" type="text/css" href="./styles.css"/>
+<link rel="shortcut icon" href="./favicon.ico" type="image/x-icon" />
 </head>
 
 <body>
@@ -61,19 +62,25 @@
    these lists (and other project lists), as well view the list
    archives, <a
    href="http://viewvc.tigris.org/servlets/ProjectMailingListList";
-   >here</a>.</p>
+   >here</a>.  If you are having problems with ViewVC, consider
+   checking our <a href="./faq.html">Frequently Asked Questions</a>
+   page and list of <a
+   href="http://viewvc.tigris.org/issues/buglist.cgi?component=viewvc&amp;issue_status=UNCONFIRMED&amp;issue_status=NEW&amp;issue_status=STARTED&amp;issue_status=REOPENED";
+   >open issues</a> before mail our users@ list &mdash; perhaps the
+   information you need is already available in one of those
+   places.</p>
 
-<p>If you're seeking interaction that's a little more real-time, use
-   your favorite IRC client to pop into <tt><a
+<p>If you prefer interaction that's a little more real-time, use your
+   favorite IRC client to pop into <tt><a
    href="irc://irc.freenode.net/viewvc">#viewvc</a></tt> on
    irc.freenode.net.</p>
 
 <p>So, to summarize:</p>
 
 <ul>
-<li>Email, developer chatter &mdash; &#100;&#101;&#118;&#64;&#118;&#105;&#101;&#119;&#118;&#99;&#46;&#116;&#105;&#103;&#114;&#105;&#115;&#46;&#111;&#114;&#103;</li>
-<li>Email, user chatter &mdash; &#117;&#115;&#101;&#114;&#115;&#64;&#118;&#105;&#101;&#119;&#118;&#99;&#46;&#116;&#105;&#103;&#114;&#105;&#115;&#46;&#111;&#114;&#103;</li>
-<li>IRC &mdash; irc.freenode.net, #viewvc</li>
+<li>To discuss ViewVC installation, configuration, and behavior:  &#117;&#115;&#101;&#114;&#115;&#64;&#118;&#105;&#101;&#119;&#118;&#99;&#46;&#116;&#105;&#103;&#114;&#105;&#115;&#46;&#111;&#114;&#103;</li>
+<li>To discuss the development of ViewVC itself or submit patches:  &#100;&#101;&#118;&#64;&#118;&#105;&#101;&#119;&#118;&#99;&#46;&#116;&#105;&#103;&#114;&#105;&#115;&#46;&#111;&#114;&#103;</li>
+<li>To discussion anything ViewVC-related in real time:  irc.freenode.net, #viewvc</li>
 </ul>
 
 </div> <!-- section-body -->

Modified: trunk/viewvc.org/contributing.html
==============================================================================
--- trunk/viewvc.org/contributing.html	(original)
+++ trunk/viewvc.org/contributing.html	Mon Nov  3 09:57:02 2008
@@ -1,10 +1,11 @@
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
                       "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";>
-<html>
+<html xmlns="http://www.w3.org/1999/xhtml";>
 <head>
 <title>ViewVC: Contributing</title>
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
 <link rel="stylesheet" type="text/css" href="./styles.css"/>
+<link rel="shortcut icon" href="./favicon.ico" type="image/x-icon" />
 </head>
 
 <body>

Modified: trunk/viewvc.org/download.html
==============================================================================
--- trunk/viewvc.org/download.html	(original)
+++ trunk/viewvc.org/download.html	Mon Nov  3 09:57:02 2008
@@ -1,10 +1,11 @@
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
                       "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";>
-<html>
+<html xmlns="http://www.w3.org/1999/xhtml";>
 <head>
 <title>ViewVC: Download</title>
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
 <link rel="stylesheet" type="text/css" href="./styles.css"/>
+<link rel="shortcut icon" href="./favicon.ico" type="image/x-icon" />
 </head>
 
 <body>
@@ -74,7 +75,7 @@
 <p>The source code for ViewVC is maintained in a Subversion repository
    at Tigris.org.  You can checkout the trunk of our development tree
    from <a href="http://viewvc.tigris.org/svn/viewvc/trunk/";
-   ><tt>http://viewvc.tigris.org/svn/viewvc/trunk/</tt></a>.  You'll
+   >http://viewvc.tigris.org/svn/viewvc/trunk/</a>.  You'll
    need to provide your Tigris.org username and password when so
    prompted, or, if you don't have a Tigris.org account, use "guest"
    as the username (with no, or an empty, password).</p>

Modified: trunk/viewvc.org/faq.html
==============================================================================
--- trunk/viewvc.org/faq.html	(original)
+++ trunk/viewvc.org/faq.html	Mon Nov  3 09:57:02 2008
@@ -1,10 +1,11 @@
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
                       "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";>
-<html>
+<html xmlns="http://www.w3.org/1999/xhtml";>
 <head>
 <title>ViewVC: Frequently Asked Questions (FAQ)</title>
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
 <link rel="stylesheet" type="text/css" href="./styles.css"/>
+<link rel="shortcut icon" href="./favicon.ico" type="image/x-icon" />
 </head>
 
 <body>
@@ -45,26 +46,23 @@
 </td>
 <td id="pagecolumn2">
 
-<div class="notice">
-<p>This FAQ is, shall we say, a work in progress.  At the moment, it's
-   pretty bare.  Please bare &mdash; er, <strong>bear</strong> with us
-   as we slowly populate this document.</p>
-</div>
-
 <div class="section">
 
-<!-- ###################################################################### -->
+<!-- ##################################################################### -->
 <h2 id="faq-q">Questions</h2>
-<!-- ###################################################################### -->
+<!-- ##################################################################### -->
 
 <div class="section-body">
 
-<!-- #--------------------------------------------------------------------# -->
+<!-- #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# -->
 <h3 class="faq-section" id="faq-q-general">General Usage</h3>
-<!-- #--------------------------------------------------------------------# -->
+<!-- #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# -->
 
 <ul>
 
+<li><a href="#installation">Where does the installation documentation,
+    if any, live?</a></li>
+
 <li><a href="#authz-support">Does ViewVC support path-based
     authorization, such as Subversion's authz-file mechanism?</a></li>
 
@@ -80,9 +78,9 @@
 
 </ul>
 
-<!-- #--------------------------------------------------------------------# -->
+<!-- #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# -->
 <h3 class="faq-section" id="faq-q-cvs">CVS Browsing</h3>
-<!-- #--------------------------------------------------------------------# -->
+<!-- #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# -->
 
 <ul>
 
@@ -92,7 +90,8 @@
 <li><a href="#comalformedoutput">What causes "Error: COMalformedOutput: Unable to
     find filename in co output stream"?</a></li>
 
-<li><a href="#">What causes "Error: error during rlog: 0x100"?</a></li>
+<li><a href="#error-during-rlog">What causes "Error: error during
+    rlog: 0x100"?</a></li>
 
 <li><a href="#missing-files">Why do my directories have no files in them?</a></li>
 
@@ -102,9 +101,9 @@
 
 </ul>
 
-<!-- #--------------------------------------------------------------------# -->
+<!-- #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# -->
 <h3 class="faq-section" id="faq-q-svn">Subversion Browsing</h3>
-<!-- #--------------------------------------------------------------------# -->
+<!-- #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# -->
 
 <ul>
 
@@ -116,21 +115,37 @@
 
 </ul>
 
-<!-- #--------------------------------------------------------------------# -->
+<!-- #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# -->
 </div>
 </div>
 
 <div class="section">
 
-<!-- ###################################################################### -->
+<!-- ##################################################################### -->
 <h2 id="faq-a">Answers</h2>
-<!-- ###################################################################### -->
+<!-- ##################################################################### -->
 
 <div class="section-body">
 
-<!-- #--------------------------------------------------------------------# -->
+<!-- #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# -->
 <h3 class="faq-section" id="faq-a-general">General Usage</h3>
-<!-- #--------------------------------------------------------------------# -->
+<!-- #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# -->
+
+<div id="installation">
+<p class="faq-atitle">Where does the installation documentation, if
+   any, live?</p>
+
+<p>ViewVC's installation how-to documentation lives in the INSTALL
+   file located in the root of the ViewVC source code distribution.
+   The most recent version of this document (which may cover
+   unreleased ViewVC versions) can be found
+   at <a href="http://viewvc.tigris.org/source/browse/*checkout*/viewvc/trunk/INSTALL";
+   >http://viewvc.tigris.org/source/browse/*checkout*/viewvc/trunk/INSTALL</a>.
+   If you are upgrading an existing ViewVC instance, you'll also want
+   to read the upgrade documentation, found
+   at <a href="http://viewvc.tigris.org/source/browse/*checkout*/viewvc/trunk/docs/upgrading-howto.html";
+   >http://viewvc.tigris.org/source/browse/*checkout*/viewvc/trunk/docs/upgrading-howto.html</a>.</p>
+</div>
 
 <div id="authz-support">
 <p class="faq-atitle">Does ViewVC support path-based authorization,
@@ -204,9 +219,9 @@
    additional configuration.</p>
 </div>
 
-<!-- #--------------------------------------------------------------------# -->
+<!-- #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# -->
 <h3 class="faq-section" id="faq-a-cvs">CVS Browsing</h3>
-<!-- #--------------------------------------------------------------------# -->
+<!-- #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# -->
 
 <div id="rlog-output-ended-early">
 
@@ -235,7 +250,7 @@
 
 </div>
 
-<div id="">
+<div id="error-during-rlog">
 <p class="faq-atitle">What causes "Error: error during rlog:
    0x100"?</p>
 
@@ -251,7 +266,11 @@
 <ul>
 <li>Some folks mistakenly point ViewVC's configuration bits to their
     CVS working copies.  But ViewVC isn't a working copy browser
-    &mdash; it's a <em>repository</em> browser.</li>
+    &mdash; it's a <em>repository</em> browser.  If you don't know
+    the difference, here's a tip that might help:  CVS repositories
+    are directories trees filled with files that end with
+    "<code>,v</code>".  If your directory isn't filled with "comma
+    vee" files, it probably is <em>not</em> a CVS repository.</li>
 <li>&hellip; <!-- TODO --></li>
 </ul>
 </div>
@@ -268,11 +287,12 @@
    solution is to try to get versions of CVS/CVSNT and ViewVC which
    are better aligned, which generally means upgrading ViewVC (which
    is probably less disruptive than downgrading your version control
-   system).</p> </div>
+   system).</p>
+</div>
 
-<!-- #--------------------------------------------------------------------# -->
+<!-- #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# -->
 <h3 class="faq-section" id="faq-a-svn">Subversion Browsing</h3>
-<!-- #--------------------------------------------------------------------# -->
+<!-- #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# -->
 
 <div id="no-module-named-svn">
 <p class="faq-atitle">What causes "Error: ImportError: No module named
@@ -323,10 +343,10 @@
          the remote repository aren't cached in
          <code>~VIEWVC_USER/.subversion</code> (where
          <code>VIEWVC_USER</code> is the system user as whom ViewVC
-         runs), stuff won't work.</p>
+         runs), stuff won't work.</p></li>
 </ul>
 
-<!-- #--------------------------------------------------------------------# -->
+<!-- #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# -->
 </div>
 </div>
 

Modified: trunk/viewvc.org/images/title.jpg
==============================================================================
Binary files. No diff available.

Modified: trunk/viewvc.org/images/title.xcf
==============================================================================
Binary files. No diff available.

Modified: trunk/viewvc.org/index.html
==============================================================================
--- trunk/viewvc.org/index.html	(original)
+++ trunk/viewvc.org/index.html	Mon Nov  3 09:57:02 2008
@@ -1,10 +1,11 @@
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
                       "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";>
-<html>
+<html xmlns="http://www.w3.org/1999/xhtml";>
 <head>
 <title>ViewVC: Repository Browsing</title>
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
 <link rel="stylesheet" type="text/css" href="./styles.css"/>
+<link rel="shortcut icon" href="./favicon.ico" type="image/x-icon" />
 </head>
 
 <body>
@@ -75,10 +76,7 @@
       href="http://www.akhphd.au.dk/~bertho/cvsgraph/";>CvsGraph</a>)
       (<em>CVS only</em>).</li>
 
-  <li>Syntax highlighting support (via integration with <a
-      href="http://www.codento.com/people/mtr/genscript/";>GNU
-      enscript</a> or
-      <a href="http://www.andre-simon.de/";>Highlight</a>).</li>
+  <li>Syntax highlighting support.</li>
 
   <li><a href="http://www.mozilla.org/projects/bonsai/";>Bonsai</a>-like
       repository query facilities.</li>

Modified: trunk/viewvc.org/nightly/build-viewvc-snapshot
==============================================================================
--- trunk/viewvc.org/nightly/build-viewvc-snapshot	(original)
+++ trunk/viewvc.org/nightly/build-viewvc-snapshot	Mon Nov  3 09:57:02 2008
@@ -1,37 +1,119 @@
-#!/bin/sh
+#!/usr/bin/env python
 
-DATE=`date +"%Y%m%d"`
-EXPORTDIR="viewvc-${DATE}"
-PUBLISH_DIR=/www/viewvc/nightly
-
-# export HEAD of trunk
-svn export --quiet http://viewvc.tigris.org/svn/viewvc/trunk ${EXPORTDIR} --username guest --password ""
-
-# use Python to determine the version number, reading it from 
-# viewvc.__version__
-cd $EXPORTDIR/lib
-VERSION=`python -c "import viewvc; print viewvc.__version__"`
-TARGET=viewvc-${VERSION}-${DATE}
-
-# make a release
-cd ../tools
-./make-release ${TARGET}
-
-# remove results of last build
-rm -rf ${PUBLISH_DIR}/viewvc*.tar.gz ${PUBLISH_DIR}/viewvc*.zip ${PUBLISH_DIR}/index.html
-
-# publish new build
-mv ${TARGET}.* ${PUBLISH_DIR}
-cd ../..
+import sys
+import os
+import time
+import urllib
+import shutil
+import getopt
+import tempfile
+
+  
+class BuildError(Exception):
+  def __init__(self, message):
+    self.message = message
+  def __str__(self):
+    return self.message
+  
+
+def make_release(branch, export_dir, publish_dir, root_url, username, password):
+    
+  # Export the requested ViewVC source tree.
+  cmd = "svn export --quiet '%s/%s' %s " \
+        "--username '%s' --password '%s' --non-interactive --force" \
+        % (root_url, urllib.quote(branch), export_dir, username, password)
+  sys.stdout.write("Running: %s\n" % (cmd))
+  os.system(cmd)
+
+  # Get the version number from ViewVC itself.
+  sys.path.insert(0, os.path.join(export_dir, 'lib'))
+  try:
+    import viewvc
+  except ImportError:
+    raise BuildError("Unable to import viewvc module; export failed?")
+  version = viewvc.__version__
+  del sys.modules['viewvc']
+  del viewvc
+  del sys.path[0]
+
+  # Now, use ViewVC tools to make the distribution archives.
+  localtime = time.localtime()
+  date = "%4d%02d%02d" % (localtime[0], localtime[1], localtime[2])
+  distversion = "viewvc-%s" % (version)
+  distname = "%s-%s" % (distversion, date)
+  gzip_name = distname + '.tar.gz'
+  zip_name = distname + '.zip'
+  curdir = os.getcwd()
+  try:
+    os.chdir(os.path.join(export_dir, 'tools'))
+    os.system('./make-release %s' % (distname))
+  finally:
+    os.chdir(curdir)
+
+  # Finally, return the locations of the archive files we've built.
+  gzip_file = os.path.join(export_dir, 'tools', gzip_name)
+  if not os.path.exists(gzip_file):
+    gzip_file = None
+  zip_file = os.path.join(export_dir, 'tools', zip_name)
+  if not os.path.exists(zip_file):
+    zip_file = None
+
+  # Remove superceded archives.
+  if gzip_file or zip_file:
+    dirents = os.listdir(publish_dir)
+    for dirent in dirents:
+      if dirent.startswith(distversion) \
+             and ((dirent.endswith('.tar.gz') and gzip_file) \
+                  or (dirent.endswith('.zip') and zip_file)):
+        os.unlink(dirent)
+
+  # Install the new archives.
+  if gzip_file:
+    os.rename(gzip_file, os.path.join(publish_dir, gzip_name))
+  if zip_file:
+    os.rename(zip_file, os.path.join(publish_dir, zip_name))
+    
+  # Return our archive names.
+  return gzip_file and gzip_name or None, zip_file and zip_name or None
+
+
+def publish_releases(branches, publish_dir, root_url, username, password):
+  new_index_contents = get_html_index_header()
+  for branch in branches:
+    sys.stdout.write("Beginning build for branch '%s'.\n" % (branch))
+    export_dir = None
+    try:
+      export_dir = tempfile.mkdtemp("", "viewvc-nightly-")
+      gzip_file, zip_file = make_release(branch, export_dir, publish_dir,
+                                         root_url, username, password)
+      new_index_contents = new_index_contents + """
+<p>Build of %s:</p>
+<ul>
+<li><a href="%s">%s</a></li>
+<li><a href="%s">%s</a></li>
+</ul>
+""" % (branch, urllib.quote(gzip_file), gzip_file,
+       urllib.quote(zip_file), zip_file)
+    finally:
+      if export_dir:
+        sys.stdout.write("Removing temporary directory '%s'.\n" % (export_dir))
+        shutil.rmtree(export_dir)
+    sys.stdout.write("Finished build for branch '%s'.\n" % (branch))
+
+  new_index_contents = new_index_contents + get_html_index_footer()
+  open(os.path.join(publish_dir, 'index.html'), 'w').write(new_index_contents)
+       
 
-cat > ${PUBLISH_DIR}/index.html <<EOF
+def get_html_index_header():
+  return """
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
                       "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";>
-<html>
+<html xmlns="http://www.w3.org/1999/xhtml";>
 <head>
 <title>ViewVC: Nightly Snapshots</title>
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
 <link rel="stylesheet" type="text/css" href="../styles.css"/>
+<link rel="shortcut icon" href="../favicon.ico" type="image/x-icon" />
 </head>
 
 <body>
@@ -60,7 +142,7 @@
 <h4>On this page:</h4>
 
 <ul id="bookmarks">
-  <li><a href="#sec-snapshots">Snapshots</a></li>
+  <li><a href="%s">Snapshots</a></li>
 </ul>
 
 <p><a href="http://validator.w3.org/check?uri=referer";><img
@@ -72,19 +154,13 @@
 <td id="pagecolumn2">
 
 <div class="section">
-
 <h2 id="sec-snapshots">Snapshots</h2>
-
 <div class="section-body">
-<ul>
-EOF
+""" % ("#sec-snapshots")
 
-echo "<li><a href=\"${TARGET}.tar.gz\">${TARGET}.tar.gz</a></li>" >> ${PUBLISH_DIR}/index.html
-echo "<li><a href=\"${TARGET}.zip\">${TARGET}.zip</a></li>" >> ${PUBLISH_DIR}/index.html
-
-cat >> ${PUBLISH_DIR}/index.html <<EOF
-</ul>
 
+def get_html_index_footer():
+  return """
 </div> <!-- section-body -->
 </div> <!-- section -->
 
@@ -93,7 +169,51 @@
 </table>
 </body>
 </html>
-EOF
+"""
+
 
-# more cleanup
-rm -rf ${EXPORTDIR}
+def usage_and_exit(errmsg=None):
+  stream = errmsg and sys.stderr or sys.stdout
+  progname = os.path.basename(sys.argv[0])
+  stream.write("""%s -- nightly ViewVC build generation
+  
+Usage: %s [OPTIONS] PUBLISH-DIR BRANCH ...
+
+Build an archive from one or more BRANCH in the ViewVC source
+repository, dropping them into PUBLISH-DIR alongside an index.html
+file that name and links to them.
+
+Options:
+   --help (-h, -?)  : Show this usage message
+   --username       : Username used for source export
+   --password       : Password used for source export
+   --root-url       : Alternative source repository root URL
+
+""" % (progname, progname))
+  if errmsg:
+    stream.write("ERROR: %s\n" % (errmsg))
+  sys.exit(errmsg and 1 or 0)
+
+
+if __name__ == "__main__":
+  root_url = "http://viewvc.tigris.org/svn/viewvc";
+  username = password = ""
+  opts, args = getopt.getopt(sys.argv[1:], 'h?',
+                             ['help', 'username=', 'password=', 'root-url='])
+  for name, value in opts:
+    if name == '-h' or name == '-?' or name == '--help':
+      usage_and_exit()
+    elif name == '--username':
+      username = value
+    elif name == '--password':
+      password = value
+    elif name == '--root-url':
+      root_url = value
+  argc = len(args)
+  if argc < 2:
+    usage_and_exit("Not enough arguments")
+  try:
+    publish_releases(args[1:], args[0], root_url, username, password)
+  except BuildError, e:
+    sys.stderr.write(str(e) + "\n")
+    sys.exit(1)

Modified: trunk/viewvc.org/styles.css
==============================================================================
--- trunk/viewvc.org/styles.css	(original)
+++ trunk/viewvc.org/styles.css	Mon Nov  3 09:57:02 2008
@@ -1,15 +1,13 @@
 body {
-    background-color: rgb(180,193,205);
-    background-image: url('./images/bg-grad.jpg');
-    background-repeat: repeat-x;
+    background-color: #b2d388;
     color: black;
     font-family: trebuchet ms, arial, sans-serif;
     margin: 0;
 }
 
 #title {
-    background-color: rgb(100,128,150);
-    height: 60px;
+    background-color: #739c3e;
+    height: 70px;
 }
 
 img {
@@ -27,7 +25,7 @@
 }
 
 h2 {
-    background-color: rgb(204,213,221);
+    background-color: #94bd5e;
     padding: 2px 0.5em 2px 0.5em;
     margin: 0;
     border-bottom: dotted 1px rgb(180,193,205);

Modified: trunk/viewvc.org/who.html
==============================================================================
--- trunk/viewvc.org/who.html	(original)
+++ trunk/viewvc.org/who.html	Mon Nov  3 09:57:02 2008
@@ -1,10 +1,11 @@
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
                       "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";>
-<html>
+<html xmlns="http://www.w3.org/1999/xhtml";>
 <head>
 <title>ViewVC: About</title>
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
 <link rel="stylesheet" type="text/css" href="./styles.css"/>
+<link rel="shortcut icon" href="./favicon.ico" type="image/x-icon" />
 </head>
 
 <body>
@@ -166,9 +167,9 @@
 <p>The ViewVC website was designed by <a
    href="http://www.cmichaelpilato.com/";>C. Michael Pilato</a>.  All
    HTML was hand-edited in Emacs, and the little splashes of graphical
-   goodness owe their existence to Adobe PhotoShop.  Textual content
-   for the site is mostly the work of Greg Stein, but has been tweaked
-   through the ages by various ViewVC contributors.</p>
+   goodness owe their existence to Open Office and the Gimp.  Textual
+   content for the site is mostly the work of Greg Stein, but has been
+   tweaked through the ages by various ViewVC contributors.</p>
 
 </div> <!-- section-body -->
 </div> <!-- section -->

Modified: trunk/windows/README
==============================================================================
--- trunk/windows/README	(original)
+++ trunk/windows/README	Mon Nov  3 09:57:02 2008
@@ -1,301 +1,324 @@
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 DESCRIPTION
 
-This file contains special instructions for setting up ViewVC on Windows. It
-will take you through a basic installation and tell you how to set up optional
-features like code colorizing and the MySQL commit database.
+This file contains special instructions for setting up ViewVC on
+Windows. It will take you through a basic installation and tell you
+how to set up optional features like code colorizing and the MySQL
+commit database.
 
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 REQUIREMENTS
 
 ViewVC requires the Python interpreter which you can download from
 
-  http://python.org/
+   http://python.org/
 
 and the Python for Windows Extensions which are at
 
-  http://sourceforge.net/projects/pywin32/
+   http://sourceforge.net/projects/pywin32/
 
-For CVS support, ViewVC also requires that the CVSNT client (cvs.exe) OR the
-RCS tools (rlog.exe, rcsdiff.exe, and co.exe) be installed on your computer.
-CVSNT is available from
+For CVS support, ViewVC also requires that the CVSNT client (cvs.exe)
+OR the RCS tools (rlog.exe, rcsdiff.exe, and co.exe) be installed on
+your computer.  CVSNT is available from
 
-  http://www.cvsnt.org/wiki
+   http://www.cvsnt.org/wiki
 
 and RCS can be obtained from:
 
-  http://www.cs.purdue.edu/homes/trinkle/RCS/
+   http://www.cs.purdue.edu/homes/trinkle/RCS/
 
-For Subversion support, you'll need to have the Subversion Python bindings
-installed. Binaries are available from the Subversion website at:
+For Subversion support, you'll need to have the Subversion Python
+bindings installed. Binaries are available from the Subversion website
+at:
 
-  http://subversion.tigris.org/servlets/ProjectDocumentList?folderID=91
+   http://subversion.tigris.org/servlets/ProjectDocumentList?folderID=91
 
-Note that if you use binaries, you have to be running the same version of
-python as the binaries were built for. For example, you cannot use Subversion
-bindings built for Python 2.3 with Python 2.4. Instructions for building
-the binaries from source are available here:
+Note that if you use binaries, you have to be running the same version
+of python as the binaries were built for. For example, you cannot use
+Subversion bindings built for Python 2.3 with Python 2.4. Instructions
+for building the binaries from source are available here:
 
-  http://svn.collab.net/repos/svn/trunk/subversion/bindings/swig/INSTALL
+   http://svn.collab.net/repos/svn/trunk/subversion/bindings/swig/INSTALL
 
-The Subversion bindings also require you to have diff.exe installed in a
-directory on your system PATH. diff.exe is available as part of the GnuWin32
-project's DiffUtils package at http://gnuwin32.sf.net/.
+The Subversion bindings also require you to have diff.exe installed in
+a directory on your system PATH. diff.exe is available as part of the
+GnuWin32 project's DiffUtils package at http://gnuwin32.sf.net/.
 
-Once you've got Python and CVSNT or RCS or the Subversion bindings installed,
-you can test out ViewVC before you install it by running:
+Once you've got Python and CVSNT or RCS or the Subversion bindings
+installed, you can test out ViewVC before you install it by running:
 
-  python bin\standalone.py -r <PATH_TO_REPOSITORY>
+   python bin\standalone.py -r <PATH_TO_REPOSITORY>
 
-The standalone server has a number of features (including a GUI interface)
-which you can find out about by running
+The standalone server has a number of features (including a GUI
+interface) which you can find out about by running
 
-  python bin\standalone.py --help
+   python bin\standalone.py --help
 
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 BASIC INSTALLATION
 
 Run the ViewVC install script with
 
-  python viewvc-install
+   python viewvc-install
 
-The script will copy the source files into an installation directory that you
-specify, store some path information, and compile the ViewVC library files into
-Python bytecode.
-
-After the installation is finished you will need to edit the viewvc.conf file
-in the folder you installed to. The comments in that file tell you exactly what
-to do.
-
-With the config file set up you should be able to double-click standalone.py
-and access your repository with a web browser.
-
-See the sections below for information on setting up optional features and
-troubleshooting. From here on <PYTHON_DIR> will stand for the Python root
-directory (usually something like C:\Python22) and <VIEWVC_INSTALL_DIR> will
-represent the directory where ViewVC has been installed to (default is
-C:\Program Files\viewvc-VERSION).
+The script will copy the source files into an installation directory
+that you specify, store some path information, and compile the ViewVC
+library files into Python bytecode.
+
+After the installation is finished you will need to edit the
+viewvc.conf file in the folder you installed to. The comments in that
+file tell you exactly what to do.
+
+With the config file set up you should be able to double-click
+standalone.py and access your repository with a web browser.
+
+See the sections below for information on setting up optional features
+and troubleshooting. From here on <PY_DIR> will stand for the Python
+root directory (usually something like C:\Python22) and
+<VIEWVC_INSTALL_DIR> will represent the directory where ViewVC has
+been installed to (default is C:\Program Files\viewvc-VERSION).
 
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 SERVER CONFIGURATION
 
-If you want to make ViewVC available to a network (rather than using it on a
-standalone machine), you will need to configure a web server to host it. This
-section includes instructions for setting up ViewVC on two commonly used
-Windows web servers, IIS and Apache.
-
-With IIS, you can run ViewVC in CGI mode or ASP mode (or both modes). ASP mode
-gives better performance (faster page loads), but is harder to set up and may
-be less stable. CGI mode is stable and easy to set up but slower.
+If you want to make ViewVC available to a network (rather than using
+it on a standalone machine), you will need to configure a web server
+to host it. This section includes instructions for setting up ViewVC
+on two commonly used Windows web servers, IIS and Apache.
+
+With IIS, you can run ViewVC in CGI mode or ASP mode (or both
+modes). ASP mode gives better performance (faster page loads), but is
+harder to set up and may be less stable. CGI mode is stable and easy
+to set up but slower.
 
-On Apache, you can use CGI mode or Mod_Python mode or both modes at once.
-Mod_Python mode is faster than CGI mode.
+On Apache, you can use CGI mode or Mod_Python mode or both modes at
+once.  Mod_Python mode is faster than CGI mode.
 
 CGI Mode On IIS
 
-  1) Copy viewvc.cgi and query.cgi from <VIEWVC_INSTALL_DIR>\bin\cgi to a
-  folder that is accessible through your web server.
+   1) Copy viewvc.cgi and query.cgi from <VIEWVC_INSTALL_DIR>\bin\cgi to a
+      folder that is accessible through your web server.
 
-  2) Start up the IIS "Internet Services Manager" and right click a virtual
-  server or virtual directory that contains the files you just copied. Choose
-  "Properties" from the context menu that appears.
-
-  3) On the properties dialog that appears, navigate to [Home | Virtual]
-  Directory -> Application Settings -> Configuration. This will bring up
-  another dialog box called "Application Configuration".
-
-  4) On the "App Mappings" tab choose "Add". Fill in the following information
-
-                 Executable:  <PYTHON_DIR>\python.exe "%s"
-                  Extension:  cgi
-              Script Engine:  checked
-     Check that file exists:  unchecked
-
-  That is all. Assuming you've set up viewvc.conf to point to your
-  repositories, the CGI pages should run. See the Troubleshooting section below
-  if there are any problems.
+   2) Start up the IIS "Internet Services Manager" and right click a
+      virtual server or virtual directory that contains the files you
+      just copied. Choose "Properties" from the context menu that
+      appears.
+
+   3) On the properties dialog that appears, navigate to [Home |
+      Virtual] Directory -> Application Settings ->
+      Configuration. This will bring up another dialog box called
+      "Application Configuration".
+
+   4) On the "App Mappings" tab choose "Add". Fill in the following
+      information:
+
+                    Executable:  <PY_DIR>\python.exe "%s"
+                     Extension:  cgi
+                 Script Engine:  checked
+        Check that file exists:  unchecked
+
+   That is all. Assuming you've set up viewvc.conf to point to your
+   repositories, the CGI pages should run. See the Troubleshooting
+   section below if there are any problems.
 
 ASP Mode On IIS
 
-  In order to run ViewVC with ASP, you will need to enable Python ActiveX
-  scripting and to install the included Aspfool ISAPI filter on whatever
-  virtual server is being used to serve the viewvc pages. Step by step
-  instructions follow below. Aspfool is located in the windows\aspfool folder.
-
-  To set up ASP mode, follow these steps:
-
-  1) Run <PYTHON_DIR>\Lib\site-packages\win32comext\axscript\client\pyscript.py
-  to register Python as an ASP scripting language. (More documentation on this
-  is at http://www.python.org/windows/win32com/ActiveXScripting.html)
-
-  2) Copy the viewvc.asp and query.asp files from
-  <VIEWVC_INSTALL_DIR>\bin\asp to a folder that is accessible through your web
-  server.
+   In order to run ViewVC with ASP, you will need to enable Python
+   ActiveX scripting and to install the included Aspfool ISAPI filter
+   on whatever virtual server is being used to serve the viewvc
+   pages. Step by step instructions follow below. Aspfool is located
+   in the windows\aspfool folder.
+
+   To set up ASP mode, follow these steps:
+
+   1) Run <PY_DIR>\Lib\site-packages\win32comext\axscript\client\pyscript.py
+      to register Python as an ASP scripting language. (More
+      documentation on this is at
+      http://www.python.org/windows/win32com/ActiveXScripting.html)
+
+   2) Copy the viewvc.asp and query.asp files from
+      <VIEWVC_INSTALL_DIR>\bin\asp to a folder that is accessible
+      through your web server.
+
+   3) Start up the IIS "Internet Services Manager" and right click on
+      the virtual server that contains the files you just
+      copied. Choose "Properties" from the context menu that appears.
+
+   4) On the properties dialog that appears, click the "ISAPI Filters"
+      tab.  Click the "add" button and enter the following
+      information:
 
-  3) Start up the IIS "Internet Services Manager" and right click on the
-  virtual server that contains the files you just copied. Choose "Properties"
-  from the context menu that appears.
+         Filter Name:  aspfool
+          Executable:  aspfool.dll
 
-  4) On the properties dialog that appears, click the "ISAPI Filters" tab.
-  Click the "add" button and enter the following information:
-
-     Filter Name:  aspfool
-      Executable:  aspfool.dll
-
-  After you save these changes, the ViewVC ASP pages should begin to work.
+   After you save these changes, the ViewVC ASP pages should begin to
+   work.
 
 CGI Mode on Apache
 
-  Follow the instructions under "Apache Configuration" in the ViewVC INSTALL
-  file.
+   Follow the instructions under "Apache Configuration" in the ViewVC
+   INSTALL file.
 
 Mod_Python Mode on Apache
 
-  There are probably ten thousand different ways to set up Apache, mod_python,
-  and ViewVC together. Here are some instructions that work for me using
-  Mod_Python 3.0.3 and Apache 2.0.46. If any Apache gurus want to contribute
-  better instructions, I'd be happy to include them here.
-
-  1) Run the win32 mod_python installer from www.modpython.org.
-
-  2) Add the following line to the "Global Environment" section of httpd.conf
+   There are probably ten thousand different ways to set up Apache,
+   mod_python, and ViewVC together. Here are some instructions that
+   work for me using Mod_Python 3.0.3 and Apache 2.0.46. If any Apache
+   gurus want to contribute better instructions, I'd be happy to
+   include them here.
+
+   1) Run the win32 mod_python installer from www.modpython.org.
+
+   2) Add the following line to the "Global Environment" section of
+      httpd.conf:
+
+         LoadModule python_module modules/mod_python.so
+
+   3) Copy viewvc.py, query.py, handler.py, and .htaccess from
+      <VIEWVC_INSTALL_DIR>\bin\mod_python to a folder being served by
+      apache. Make sure overrides are allowed in this folder. The
+      relevant parent directory in httpd.conf should have
+      "AllowOverride All" set, or at least "AllowOverride FileInfo
+      Options".
 
-     LoadModule python_module modules/mod_python.so
-
-  3) Copy viewvc.py, query.py, handler.py, and .htaccess from
-  <VIEWVC_INSTALL_DIR>\bin\mod_python to a folder being served by apache. Make
-  sure overrides are allowed in this folder. The relevant parent directory in
-  httpd.conf should have "AllowOverride All" set, or at least "AllowOverride
-  FileInfo Options".
-
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 ENSCRIPT HIGHLIGHTING
 
-To use enscript, you'll have to install the enscript, libintl, libiconv, and
-sed packages from the gnuwin32 project (http://gnuwin32.sourceforge.net/).
-Detailed instructions are on their site, but here is the basic procedure.
-
-1) Extract all packages to a folder on your hard drive, for example c:\gnuwin32
-
-2) Add "c:\gnuwin32\bin" to the system "PATH" environment variable. If ViewVC
-is running as part of a system service like IIS you will need to reboot the
-computer so it is able to see the value. See the "Troubleshooting" section
-below for specific information on when a reboot is neccessary.
+To use enscript, you'll have to install the enscript, libintl,
+libiconv, and sed packages from the gnuwin32 project
+(http://gnuwin32.sourceforge.net/).  Detailed instructions are on
+their site, but here is the basic procedure.
+
+1) Extract all packages to a folder on your hard drive, for example
+   c:\gnuwin32
+
+2) Add "c:\gnuwin32\bin" to the system "PATH" environment variable. If
+   ViewVC is running as part of a system service like IIS you will
+   need to reboot the computer so it is able to see the value. See the
+   "Troubleshooting" section below for specific information on when a
+   reboot is neccessary.
 
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 BONSAI-LIKE CHECKIN DATABASE
 
-To use the checkin database, you'll need to install MySQL and the MySQL-Python
-interface. MySQL can be downloaded from www.mysql.com. The MySql-Python adapter
-is available from http://sf.net/projects/mysql-python/. Make sure to grab the
-the latest version from the "Files" section. (The "Home Page" link takes you to
-an outdated page that only links to very old versions.) Both packages come with
-GUI installers. Once you have MySQL running and set up with a username and
-password, follow these instructions to set up ViewVC.
+To use the checkin database, you'll need to install MySQL and the
+MySQL-Python interface. MySQL can be downloaded from
+www.mysql.com. The MySql-Python adapter is available from
+http://sf.net/projects/mysql-python/. Make sure to grab the the latest
+version from the "Files" section. (The "Home Page" link takes you to
+an outdated page that only links to very old versions.) Both packages
+come with GUI installers. Once you have MySQL running and set up with
+a username and password, follow these instructions to set up ViewVC.
 
 1) Open a command prompt and type these commands:
 
-   cd /d <VIEWVC_INSTALL_DIR>
-   python bin\make-database
+      cd /d <VIEWVC_INSTALL_DIR>
+      python bin\make-database
 
-The script that comes up will prompt you for the MySQL username and password
-(you should have created these during the MySQL installation), and the name of
-the database to create. The default database name "ViewVC" should be fine
-unless for some reason a database with that name already exists.
+   The script that comes up will prompt you for the MySQL username and
+   password (you should have created these during the MySQL
+   installation), and the name of the database to create. The default
+   database name "ViewVC" should be fine unless for some reason a
+   database with that name already exists.
 
-2) Enter the username, password, and database name into the [cvsdb] section of
-the <VIEWVC_INSTALL_DIR>\viewvc.conf file.
+2) Enter the username, password, and database name into the [cvsdb]
+   section of the <VIEWVC_INSTALL_DIR>\viewvc.conf file.
 
 3) At the command prompt run
 
-   python bin\cvsdbadmin rebuild <repository>
+      python bin\cvsdbadmin rebuild <REPOSITORY>
 
-where <repository> is the path to your CVS repository.
+   where <REPOSITORY> is the path to your CVS repository.
 
-4) If you want the checkin database to be dynamically updated with every
-checkin, add the following line to your CVSROOT/loginfo file:
+4) If you want the checkin database to be dynamically updated with
+   every checkin, add the following line to your CVSROOT/loginfo file:
 
-   ALL python "<VIEWVC_INSTALL_DIR>\bin\loginfo-handler" %{sVv}
+      ALL python "<VIEWVC_INSTALL_DIR>\bin\loginfo-handler" %{sVv}
 
-If you decide not to enable dynamic updates, you can periodically refresh the
-database with "python bin\cvsdbadmin update <repository>"
+   If you decide not to enable dynamic updates, you can periodically
+   refresh the database with "python bin\cvsdbadmin update <REPOSITORY>"
 
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 CVSGRAPH
 
-To use CvsGraph with ViewVC, just put cvsgraph.exe in a directory on your
-system PATH and set the use_cvsgraph option to 1 in your viewvc.conf file.
+To use CvsGraph with ViewVC, just put cvsgraph.exe in a directory on
+your system PATH and set the use_cvsgraph option to 1 in your
+viewvc.conf file.
 
 The CvsGraph home page is http://www.akhphd.au.dk/~bertho/cvsgraph/.
 
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 DOCROOT OPTIMIZATION
 
 By default ViewVC serves up image and stylesheet files in
-<VIEWVC_INSTALL_DIR>\templates\docroot\ on its own instead of relying on the
-webserver to deliver them. This simplifies web server configuration, but is
-inefficient because it means the Python interpreter has to run each time one of
-these files is downloaded. This causes ViewVC pages to load more slowly,
-especially when ViewVC is running under CGI on Windows.
+<VIEWVC_INSTALL_DIR>\templates\docroot\ on its own instead of relying
+on the webserver to deliver them. This simplifies web server
+configuration, but is inefficient because it means the Python
+interpreter has to run each time one of these files is
+downloaded. This causes ViewVC pages to load more slowly, especially
+when ViewVC is running under CGI on Windows.
 
 To make things more efficient, you can make the
-<VIEWVC_INSTALL_DIR>\templates\docroot directory available on your web server
-and then set the "docroot" value in viewvc.conf to point to the web address of
-the directory.
+<VIEWVC_INSTALL_DIR>\templates\docroot directory available on your web
+server and then set the "docroot" value in viewvc.conf to point to the
+web address of the directory.
 
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 TROUBLESHOOTING
 
-- By far the most common cause of errors in ViewVC is failure to successfully
-  execute the programs it depends on (cvs, rlog, rcsdiff, co, enscript, sed,
-  and cvsgraph). To help deal with this problem, ViewVC includes a special
-  debugging mode that displays output from the programs it executes on every
-  web page. This allows you to see error messages and other information that
-  isn't normally visible. To enable the debugging mode, change line 23 of
+- By far the most common cause of errors in ViewVC is failure to
+  successfully execute the programs it depends on (cvs, rlog, rcsdiff,
+  co, enscript, sed, and cvsgraph). To help deal with this problem,
+  ViewVC includes a special debugging mode that displays output from
+  the programs it executes on every web page. This allows you to see
+  error messages and other information that isn't normally visible. To
+  enable the debugging mode, change line 23 of
   <VIEWVC_INSTALL_DIR>\lib\debug.py from:
 
-    SHOW_CHILD_PROCESSES = 0
+     SHOW_CHILD_PROCESSES = 0
 
   to:
 
-    SHOW_CHILD_PROCESSES = 1
+     SHOW_CHILD_PROCESSES = 1
 
-  Important: You may need to restart your web server before this change takes
-  effect. See "Changes made to..." later in this section.
+  Important: You may need to restart your web server before this
+  change takes effect. See "Changes made to..." later in this section.
 
 - If you see the following error:
 
-    error: (2, 'CreateProcess', The system cannot find the file specified.')
+     error: (2, 'CreateProcess', The system cannot find the file specified.')
 
-  it means that a program ViewVC has tried to execute could not be found by
-  Windows. The fix to this is usually to install the program if it isn't
-  already installed or to update the path to the program in viewvc.conf.
-  Enabling the SHOW_CHILD_PROCESSES mode as described above can provide helpful
-  diagnostic information such as the command line ViewVC is using to invoke the
-  program and the value of the PATH environment variable in the environment
-  ViewVC is running under.
-
-- A common cause of server errors under IIS is permissions problems. You need
-  to make sure that the virtual directory containing the CGI or ASP files has
-  script execution enabled. You also need to make sure that the web server user
-  accounts (IUSR_machine_name and IWAM_machine_name, where machine_name is your
-  computer name) have read and execute access to the .asp or .cgi stub scripts,
-  the ViewVC lib/ folder, the paths where external tools like cvs, rcs,
-  enscript, sed, and cvsgraph live, and the CVS repositories. NTFS auditing
-  makes it very easy to track down permissions problems. Also look for IIS
-  messages in the event log.
-
-- Certain Apache configurations may hide some environment variables from the
-  ViewVC CGI scripts and the programs they launch. You can see whether an
-  environment variable is visible from the CGI environment by enabling the
-  SHOW_CHILD_PROCESSES debug mode described above. You can force Apache to let
-  variables through with the PassEnv directive
+  it means that a program ViewVC has tried to execute could not be
+  found by Windows. The fix to this is usually to install the program
+  if it isn't already installed or to update the path to the program
+  in viewvc.conf.  Enabling the SHOW_CHILD_PROCESSES mode as described
+  above can provide helpful diagnostic information such as the command
+  line ViewVC is using to invoke the program and the value of the PATH
+  environment variable in the environment ViewVC is running under.
+
+- A common cause of server errors under IIS is permissions
+  problems. You need to make sure that the virtual directory
+  containing the CGI or ASP files has script execution enabled. You
+  also need to make sure that the web server user accounts
+  (IUSR_machine_name and IWAM_machine_name, where machine_name is your
+  computer name) have read and execute access to the .asp or .cgi stub
+  scripts, the ViewVC lib/ folder, the paths where external tools like
+  cvs, rcs, enscript, sed, and cvsgraph live, and the CVS
+  repositories. NTFS auditing makes it very easy to track down
+  permissions problems. Also look for IIS messages in the event log.
+
+- Certain Apache configurations may hide some environment variables
+  from the ViewVC CGI scripts and the programs they launch. You can
+  see whether an environment variable is visible from the CGI
+  environment by enabling the SHOW_CHILD_PROCESSES debug mode
+  described above. You can force Apache to let variables through with
+  the PassEnv directive
   (http://httpd.apache.org/docs/mod/mod_env.html#passenv).
 
-- Changes made to environment variables, ViewVC source files and the ViewVC
-  configuration file do not always take effect immediately. The table below
-  shows what actions you need to take after changing any of these things before
-  they will have an effect.
+- Changes made to environment variables, ViewVC source files and the
+  ViewVC configuration file do not always take effect immediately. The
+  table below shows what actions you need to take after changing any
+  of these things before they will have an effect.
 
 +----------------+----------------+----------------+-------------------------+
 |                | Environment    | ViewVC         | ViewVC                  |
@@ -317,164 +340,182 @@
 |                |                |                | OR                      |
 |                |                |                | reload(viewvc) in stub  |
 +----------------+----------------+----------------+-------------------------+
-  * If standalone.py was launched from a command prompt and you set the
-  environment variable through the control panel, you'll need to open a new
-  command prompt.
+     * If standalone.py was launched from a command prompt and you set
+       the environment variable through the control panel, you'll need to
+       open a new command prompt.
 
   Notes:
 
-  Under ASP, changes made to the stub scripts inside the web root do take
-  effect immediately, you only need to take additional action when you make
-  changes to the main source files in <VIEWVC_INSTALL_DIR>\lib
-
-  To "Unload ASP App", go to the IIS properties dialog for the application
-  directory containing the ViewVC .asp files (in Internet Services Manager).
-  Switch to the [Home] | [Virtual] Directory tab and click the "Unload" button
-  under "Application Settings".
+  Under ASP, changes made to the stub scripts inside the web root do
+  take effect immediately, you only need to take additional action
+  when you make changes to the main source files in <VIEWVC_INSTALL_DIR>\lib
+
+  To "Unload ASP App", go to the IIS properties dialog for the
+  application directory containing the ViewVC .asp files (in Internet
+  Services Manager).  Switch to the [Home] | [Virtual] Directory tab
+  and click the "Unload" button under "Application Settings".
 
-  To "reload(viewvc) in stub", put these lines in one of the ASP or Mod_Python
-  stub scripts:
+  To "reload(viewvc) in stub", put these lines in one of the ASP or
+  Mod_Python stub scripts:
 
-    import viewvc
-    reload (viewvc)
+     import viewvc
+     reload (viewvc)
 
   then load the page in a web browser.
 
-- If you have problems getting ViewVC to work with mod_python, you can first
-  make sure mod_python works standalone with the testing instructions at
+- If you have problems getting ViewVC to work with mod_python, you can
+  first make sure mod_python works standalone with the testing
+  instructions at
   http://www.modpython.org/live/current/doc-html/inst-testing.html.
 
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 KNOWN ISSUES
 
 - If you see ViewVC errors like
 
-    Error parsing rlog output. Expected RCS file "c:\cvsroot\dir\file,v", found
-    "c:\cvsroot\dir\RCS/file,v"
+     Error parsing rlog output. Expected RCS file
+    "c:\cvsroot\dir\file,v", found "c:\cvsroot\dir\RCS/file,v"
 
-  it's because your RCS utilities don't recognize the RCS file suffix and are
-  treating all files specified on the command line like working copies even
-  when they end in ",v". You can fix this by including the following string in
-  your RCSINIT environment variable:
+  it's because your RCS utilities don't recognize the RCS file suffix
+  and are treating all files specified on the command line like
+  working copies even when they end in ",v". You can fix this by
+  including the following string in your RCSINIT environment variable:
 
     -x,v
 
-  Important: You may need to reboot your computer before the environment
-  variable has an effect. See "Changes made to..." in the TROUBLESHOOTING
-  section.
-
-- The GNU RCS utilities won't work with repository files that use CVSNT's
-  unicode expansion mode (-ku). Files that use this mode will show up with an
-  "rlog error: unknown expand mode u" error message in ViewVC directory
-  listings. To work around this, you can set up ViewVC to use the CVSNT
-  executable (cvs.exe) or CVSNT RCS tools (co.exe, rlog.exe, and rcsdiff.exe)
-  instead of the GNU tools.
-
-- The standalone server will not run under Cygwin Python because it does not
-  support threads. ASP pages can't be run with Cygwin Python because it does
-  not support ActiveX. To use either of these features you should install a
-  native Python interpreter.
-
-- On Windows XP and Windows 2003 Server under IIS, enscript might give an error
-  like:
-
-    enscript: couldn't open input filter "states -f
-    "K:/gnuwin32/share/enscript/hl/enscript.st" -p
-    "C://.enscript;K:/gnuwin32/share/enscript/hl" -shtml -Dcolor=1
-    -Dstyle=emacs -Dlanguage=html -Dnum_input_files=1
-    -Ddocument_title="Enscript Output" -Dtoc=0 -" for file "": No error
-    no output generated
+  Important: You may need to reboot your computer before the
+  environment variable has an effect. See "Changes made to..." in the
+  TROUBLESHOOTING section.
+
+- The GNU RCS utilities won't work with repository files that use
+  CVSNT's unicode expansion mode (-ku). Files that use this mode will
+  show up with an "rlog error: unknown expand mode u" error message in
+  ViewVC directory listings. To work around this, you can set up
+  ViewVC to use the CVSNT executable (cvs.exe) or CVSNT RCS tools
+  (co.exe, rlog.exe, and rcsdiff.exe) instead of the GNU tools.
+
+- The standalone server will not run under Cygwin Python because it
+  does not support threads. ASP pages can't be run with Cygwin Python
+  because it does not support ActiveX. To use either of these features
+  you should install a native Python interpreter.
+
+- On Windows XP and Windows 2003 Server under IIS, enscript might give
+  an error like:
+
+     enscript: couldn't open input filter "states -f
+     "K:/gnuwin32/share/enscript/hl/enscript.st" -p
+     "C://.enscript;K:/gnuwin32/share/enscript/hl" -shtml -Dcolor=1
+     -Dstyle=emacs -Dlanguage=html -Dnum_input_files=1
+     -Ddocument_title="Enscript Output" -Dtoc=0 -" for file "": No error
+     no output generated
 
   The solution is to give read & execute permissions on cmd.exe to the
-  IUSR_computername and IWAM_computername user accounts. (Enscript uses cmd.exe
-  internally to launch its little helper program, states.exe).
-
-- By default, ASP will set session cookies at each page load. ViewVC does not
-  use these cookies and they can be safely disabled. You can do this by opening
-  the IIS properties dialog for the application directory containing the ViewVC
-  .asp files. Go to the [Home] | [Virtual] Directory tab and click the
-  "Configuration" button under "Application Settings". On the dialog that comes
-  up, uncheck "Enable Session State" under "App Options" -> "Application
-  Configuration".
-
-- Python support for ASP can be a little flaky. If you get strange errors, it
-  can sometimes help to uninstall and reinstall it with pyscript.py. A number
-  of people have also encountered a problem in ActivePython 2.2 where the first
-  loads of any Python ASP page would work, but subsequent loads of the same
-  page would always return nothing (leaving the screen blank). There were a
-  number of workarounds for this problem, but the fix is to download and
-  install the latest python win32 extensions from
+  IUSR_computername and IWAM_computername user accounts. (Enscript
+  uses cmd.exe internally to launch its little helper program,
+  states.exe).
+
+- By default, ASP will set session cookies at each page load. ViewVC
+  does not use these cookies and they can be safely disabled. You can
+  do this by opening the IIS properties dialog for the application
+  directory containing the ViewVC .asp files. Go to the [Home] |
+  [Virtual] Directory tab and click the "Configuration" button under
+  "Application Settings". On the dialog that comes up, uncheck "Enable
+  Session State" under "App Options" -> "Application Configuration".
+
+- Python support for ASP can be a little flaky. If you get strange
+  errors, it can sometimes help to uninstall and reinstall it with
+  pyscript.py. A number of people have also encountered a problem in
+  ActivePython 2.2 where the first loads of any Python ASP page would
+  work, but subsequent loads of the same page would always return
+  nothing (leaving the screen blank). There were a number of
+  workarounds for this problem, but the fix is to download and install
+  the latest python win32 extensions from
   http://sourceforge.net/projects/pywin32/
 
-- ViewVC can't convert timestamps on diff pages to local time when it is used
-  with CVSNT. This is caused by a CVSNT bug, which is described at
+- ViewVC can't convert timestamps on diff pages to local time when it
+  is used with CVSNT. This is caused by a CVSNT bug, which is
+  described at
   http://www.cvsnt.org/mantis/bug_view_page.php?bug_id=0000110
 
-- Old versions of CVSNT (2.0.11 and earlier) have a bug in their rlog emulation
-  which causes them to output truncated paths to RCS files. In ViewVC, this
-  causes errors like
-
-    Error parsing rlog output. Expected RCS file "c:\cvsroot\dir\file,v", found
-    "file,v"
-
-- Old versions of CVSNT (2.0.11 and earlier) have a bug in their standalone RCS
-  tools (rlog.exe, co.exe, and rcsdiff.exe) which causes them not to properly
-  interpret arguments with spaces. This can result in ViewVC errors in
-  repositories that have spaces in file or directory names. This bug only
-  occurs when ViewVC is configured to use the standalone utilities, not when it
-  uses cvs.exe directly as it does by default.
-
-- Old versions of CVSNT (1.11.1.3-76 and earlier) don't have any RCS emulation,
-  so they can't be used as RCS parsers for ViewVC.
-
-- Very old versions of CVSNT (1.11.1.3-57g and earlier) won't work reliably
-  with our loginfo handler because they have a bug which makes them escape
-  spaces and other special characters in filenames twice. This bug can result
-  in loginfo errors or invalid data being inserted into the database.
+- Old versions of CVSNT (2.0.11 and earlier) have a bug in their rlog
+  emulation which causes them to output truncated paths to RCS
+  files. In ViewVC, this causes errors like
+
+     Error parsing rlog output. Expected RCS file
+    "c:\cvsroot\dir\file,v", found "file,v"
+
+- Old versions of CVSNT (2.0.11 and earlier) have a bug in their
+  standalone RCS tools (rlog.exe, co.exe, and rcsdiff.exe) which
+  causes them not to properly interpret arguments with spaces. This
+  can result in ViewVC errors in repositories that have spaces in file
+  or directory names. This bug only occurs when ViewVC is configured
+  to use the standalone utilities, not when it uses cvs.exe directly
+  as it does by default.
+
+- Old versions of CVSNT (1.11.1.3-76 and earlier) don't have any RCS
+  emulation, so they can't be used as RCS parsers for ViewVC.
+
+- Very old versions of CVSNT (1.11.1.3-57g and earlier) won't work
+  reliably with our loginfo handler because they have a bug which
+  makes them escape spaces and other special characters in filenames
+  twice. This bug can result in loginfo errors or invalid data being
+  inserted into the database.
 
-- Old versions of Highlight (2.4.3 and earlier) will not show line numbers
-  for .txt files or files of unknown type even when the
+- Old versions of Highlight (2.4.3 and earlier) will not show line
+  numbers for .txt files or files of unknown type even when the
   "highlight_line_numbers" option is enabled.
 
-- Highlight versions 2.4.2 and 2.4.3 start line numbering for all file types
-  at 0 instead of 1 by default. A workaround is to make ViewVC pass an
-  explicit --line-number-start=1 option to Highlight
-
-- Highlight version 2.4.4 starts line numbering for .txt files at 0 instead of
-  1. It also misinterprets the --line-number-start option for those files,
-  starting numbering one number before whatever number is specified. Since this
-  behavior does not affect unknown file types, a simple workaround is just to
-  not pass a --syntax option to Highlight for plain text files (instead of
-  passing --syntax=txt).
+- Highlight versions 2.4.2 and 2.4.3 start line numbering for all file
+  types at 0 instead of 1 by default. A workaround is to make ViewVC
+  pass an explicit --line-number-start=1 option to Highlight
+
+- Highlight version 2.4.4 starts line numbering for .txt files at 0
+  instead of 1. It also misinterprets the --line-number-start option
+  for those files, starting numbering one number before whatever
+  number is specified. Since this behavior does not affect unknown
+  file types, a simple workaround is just to not pass a --syntax
+  option to Highlight for plain text files (instead of passing
+  --syntax=txt).
 
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 THANKS
 
-- Bryan T. Vold for improving the original ViewCVS patch by adding support for
-  enscript and tarball generation.
+- Bryan T. Vold for improving the original ViewCVS patch by adding
+  support for enscript and tarball generation.
+
 - David Resnick for tracking down the cause of re_search failures in
   repositories with non-rcs files and for bringing a bug in
   sapi.AspFile.header() to my attention
-- Matt Bunch for finding a better way to address the ASP blank page problem,
-  and Keith D. Zimmerman for finding another workaround.
-- Rü Koch for reporting a bug in viewvc PATH_INFO parsing code with
-  Apache for Windows as well as Jelle Ouwerkerk and Steffen Yount for providing
-  fixes.
-- Nick Minutello and Rü Koch for providing workarounds for setting
-  enscript_library environment variable with apache. David Duminy for providing
-  the first bug report on this.
-- Gyula Faller and Tony Cook for independently coming up with CVSNT loginfo
-  handlers that accept spaces and don't rely on unix-style echo commands. Tony
-  Cook's patch also eliminated the dependency on cat.exe.
-- Mathieu Mazerolle for making the unix loginfo handler handle spaces in
-  filenames
-- Paul Russell for analyzing problems with new fields in CVSNT RCS files.
-  Terry Ninnis pgen com for coming up with a partial solution
-- Bo Berglund for tracking down the cause of a case-sensitivity issue that
-  could lead to problems in the commit database and for patiently working with
-  me to finally fix the CVSNT RCS fields problem and another problem with
-  enscript.
-- Ivo Roessling for finding and fixing a bug in the query page's commit
-  grouping code.
-- Keith D. Zimmerman for experimenting with enscript and finding some new ways
-  to make it work.
+
+- Matt Bunch for finding a better way to address the ASP blank page
+  problem, and Keith D. Zimmerman for finding another workaround.
+
+- Rü Koch for reporting a bug in viewvc PATH_INFO parsing code
+  with Apache for Windows as well as Jelle Ouwerkerk and Steffen Yount
+  for providing fixes.
+
+- Nick Minutello and Rü Koch for providing workarounds for
+  setting enscript_library environment variable with apache. David
+  Duminy for providing the first bug report on this.
+
+- Gyula Faller and Tony Cook for independently coming up with CVSNT
+  loginfo handlers that accept spaces and don't rely on unix-style
+  echo commands. Tony Cook's patch also eliminated the dependency on
+  cat.exe.
+
+- Mathieu Mazerolle for making the unix loginfo handler handle spaces
+  in filenames.
+
+- Paul Russell for analyzing problems with new fields in CVSNT RCS
+  files.  Terry Ninnis pgen com for coming up with a partial solution
+
+- Bo Berglund for tracking down the cause of a case-sensitivity issue
+  that could lead to problems in the commit database and for patiently
+  working with me to finally fix the CVSNT RCS fields problem and
+  another problem with enscript.
+
+- Ivo Roessling for finding and fixing a bug in the query page's
+  commit grouping code.
+
+- Keith D. Zimmerman for experimenting with enscript and finding some
+  new ways to make it work.

Modified: trunk/www/index.html
==============================================================================
--- trunk/www/index.html	(original)
+++ trunk/www/index.html	Mon Nov  3 09:57:02 2008
@@ -21,16 +21,7 @@
 <div class="h2">
 <h2>Latest Release</h2>
 
-<p>The most recent release of ViewVC is: <strong>1.0.4</strong></p>
-
-</div>
-
-<div class="h2">
-<h2>New IRC Channel for ViewVC</h2>
-
-<p>Use your favorite IRC client to pop into <tt><a
-   href="irc://irc.freenode.net/viewvc">#viewvc</a></tt> on
-   irc.freenode.net.</p>
+<p>The most recent release of ViewVC is: <strong>1.0.7</strong></p>
 
 </div>
 
@@ -73,6 +64,26 @@
 </div>
 
 <div class="h2">
+<h2>Wanna Talk About ViewVC?</h2>
+
+<p>If you have questions about ViewVC &mdash; how to configure it, if
+   some behavior you are seeing is expected or not, and so on &mdash;
+   send email to our users list: <a
+   href="mailto:users viewvc tigris org"
+   >users viewvc tigris org</a>.</p>
+
+<p>If you'd like to discuss the actual development of ViewVC itself,
+   or submit a patch to ViewVC's sources, you can do so on our
+   development list: <a href="mailto:dev viewvc tigris org"
+   >dev viewvc tigris org</a>.</p>
+
+<p>Finally, if you prefer realtime chatter, use your favorite IRC
+   client to pop into <tt><a href="irc://irc.freenode.net/viewvc"
+   >#viewvc</a></tt> on irc.freenode.net.</p>
+
+</div>
+
+<div class="h2">
 <h2>Screenshots</h2>
 
 <p>Who needs screenshots when you can visit and interact with running



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]