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

Compare commits

..

188 Commits

Author SHA1 Message Date
06bfb6a5fe Record merge with r2243 in git 2013-07-18 17:05:41 +04:00
vfilippov
04d3b88f6a Bug 37020
Merge with r2243 from official SVN


git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@268 6955db30-a419-402b-8a0d-67ecbb4d7f56
2009-08-20 12:43:52 +00:00
vfilippov
d85d6578da Bug 53107
For Wiki Template:cvs


git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@256 6955db30-a419-402b-8a0d-67ecbb4d7f56
2009-08-17 14:30:37 +00:00
cmpilato
eb69c40687 Merge in 1.0.9 and 1.1.2's changes.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2242 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-08-11 16:00:21 +00:00
cmpilato
6cbb3fadac Note new latest release on project homepage.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2241 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-08-11 15:58:09 +00:00
cmpilato
cc4589ab10 Rename 'Source tarballs' left-nav as 'Downloads'.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2240 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-08-11 15:26:33 +00:00
cmpilato
fcff592db3 Rename 'Subversion' left-nav as 'Source Code'.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2239 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-08-11 15:25:55 +00:00
cmpilato
694a14e0d2 Update 'Mailing Lists' left-nav link.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2238 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-08-11 15:25:14 +00:00
vfilippov
7a40e1c902 Bug 52230
A fix for potential bug when no Subversion repositories are visible



git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@206 6955db30-a419-402b-8a0d-67ecbb4d7f56
2009-07-24 17:35:25 +00:00
cmpilato
205b3b67e1 Make it easier for folks to show/hide binary file contents in the
markup/annotate views.

* templates/file.ezt
  Add some customizable template logic to hide the contents of file's with
  non-human-readable file formats.  Were Greg Stein dead, he'd be rolling
  over in his grave right now.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2228 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-07-16 19:28:29 +00:00
cmpilato
8df8e878f4 * lib/viewvc.py
(view_error): Settle for simple HTML-escaping of error messages
    rather than full htmlification.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2225 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-07-14 20:43:46 +00:00
cmpilato
f038efef96 Fix issue #422: syntax coloration dropping initial blank lines from
files.

* lib/viewvc.py
  (markup_stream_pygments): Pass stripnl=False to Pygments lexers so
    they won't strip out leading blank lines from the files they are
    colorizing.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2223 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-07-13 17:13:16 +00:00
cmpilato
52584dec66 Document some shortcomings of the path-based authz subsystem.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2222 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-07-13 14:09:17 +00:00
cmpilato
5c9f504727 * conf/viewvc.conf.dist
(allow_compress): Add a note about the speed penalty this option causes.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2220 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-07-07 14:05:12 +00:00
cmpilato
7817608915 Try to avoid content spoofing errors. Prior to this change, a URL
like this:

   http://localhost/viewvc/subversion/?view=foo%22)%20was%20passed\
   %20as%20a%20parameter.20%20Visit%20http://www.baddy.com%20to%20\
   figure%20out%20why%20(%22foo

Would result in an error page that read as follows:

   An illegal value ("foo") was passed as a parameter. Visit
   http://www.baddy.com to figure out why ("foo") was passed as a
   parameter.

(where "http://www.baddy.com" was linkified, and could potentially
point to a malicious website.)

With this change, we will avoid printing unknown parameter names, and
will show the parameter name rather its value when a bogus value is
detected.  Yes, there's admittedly some information loss here that
might be useful to the well-meaning but fat-fingered user, but the
security aspect is more important.

* lib/viewvc.py
  (_validate_param): Don't print illegal parameter names, and show the
    (legal) parameter name rather than its value when that value is
    illegal.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2217 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-07-06 19:03:06 +00:00
cmpilato
0292a54ec0 Some more around input validation, cleaning up some code and
tightening up some other bits.

* lib/viewvc.py
  (_validate_view, _validate_mimetype): New validation functions.
  (_re_validate_mimetype): Removed as unused.
  (_legal_params): Tweak the validation routines for 'view' and
    'content-type' to point to _validate_view() and _validate_mimetype(),
    respectively.
  (_validate_param): Now check for non-zero result from validation
    functions as the "valid" indicator, rather than requiring each of
    them to raise an exception in troubled times.
  (Request.run_viewvc): Lose now-unnecessary validation of
    'content-type' parameter -- the normal parameter validation stuff
    should catch problems there.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2213 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-07-06 15:05:23 +00:00
cmpilato
6c93d8c8d1 * lib/viewvc.py
(Request.run_viewvc): Move the logic that maps view=rev to
    view=revision into the parameter validation loop.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2212 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-07-06 14:48:53 +00:00
cmpilato
4f622d375b * lib/viewvc.py
(Request.run_viewvc): Use an intermediate variable to simplify some
    logic around input parameter validation.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2211 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-07-06 14:46:21 +00:00
stas
c8dbd38e0c Bug 51239
try: except: pass
FUCKING PYDON


git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@198 6955db30-a419-402b-8a0d-67ecbb4d7f56
2009-07-02 13:46:38 +00:00
stas
82ea92e4a4 Bug 44995
Unicode() string in format_log


git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@196 6955db30-a419-402b-8a0d-67ecbb4d7f56
2009-07-02 11:22:55 +00:00
vfilippov
be67aab7dc Bug 32155
Fix RSS urls in multi-repository feeds


git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@194 6955db30-a419-402b-8a0d-67ecbb4d7f56
2009-07-01 10:29:45 +00:00
cmpilato
867514f9ef Minor comment tweak.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2209 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-06-17 21:22:14 +00:00
cmpilato
b36d8584e8 * conf/viewvc.conf.dist
Comment out all the options here, forcing folks to uncomment the
  stuff they want to modify.  This allows the parsing of this file to
  be optimized toward doing less work, while maintaining a consistent
  initial presentation.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2207 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-06-17 14:32:16 +00:00
stas
8ecc076ebc Bug 37020
Fix build_commit access check


git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@186 6955db30-a419-402b-8a0d-67ecbb4d7f56
2009-06-17 12:13:05 +00:00
stas
6a0562d708 Bug 37020
debug


git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@185 6955db30-a419-402b-8a0d-67ecbb4d7f56
2009-06-16 16:54:14 +00:00
cmpilato
8e848a4025 Finish issue #415 - ModPythonServer shouldn't call sys.exit().
(This doesn't really fix anything, and hopefully doesn't break anything
either, but is merely a "good programming practice" cleanup item.)

* lib/viewvc.py
  (Request.run_viewvc): Ensure that server.redirect() is the last
    thing this function does (when it does it).

* lib/sapi.py
  (CgiServer.redirect): Avoid calling sys.exit().  Also, fix a use of
    print() that should be sys.stdout.write() (for consistency).
  (AspServer.redirect, ModPythonServer.redirect): Avoid calling sys.exit().


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2205 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-06-16 15:04:24 +00:00
vfilippov
6835e74f20 Merge with ViewVC 1.2-dev http://viewvc.tigris.org/svn/viewvc@2204
git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@184 6955db30-a419-402b-8a0d-67ecbb4d7f56
2009-06-16 14:11:20 +00:00
cmpilato
e7e8beca0a Finish issue #336 - Custom cvsgraph rendering via CGI parameters.
* conf/viewvc.conf.dist
  (allowed_cvsgraph_useropts): New option.

* lib/config.py
  (Config.set_defaults): Set default value for new
    'allowed_cvsgraph_useropts' option.

* lib/viewvc.py
  (_legal_params): Add new legal parameters: 'gflip', 'gbbox',
    'gshow', 'gleft', and 'gmaxtag'.
  (cvsgraph_make_reqopt, cvsgraph_normalize_gshow, cvsgraph_extraopts):
    New helper functions.
  (view_cvsgraph_image): Now pass the -O option set to the cvsgraph
    binary.
  (view_cvsgraph): Now pass the -O option set to the cvsgraph binary.
    Also, add new template data stuff for generating a user-configurable
    graph display options form.

* templates/graph.ezt,
* templates/docroot/styles.css
  Add template stuff for generating a user-configurable graph display
  options form.

* docs/url-reference.html,
* docs/template-authoring-guide.html
  Update documentation.

* CHANGES
  Note this change.

Patch by:  Bertho Stultiens <bertho{_AT_}j.auh.dk> (originally),
           Jene Jasper <tigris{_AT_}shadowland.demon.nl> (port to 1.1.x),
           me (tweaks for official ViewVC inclusion)


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2204 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-06-15 20:18:35 +00:00
cmpilato
c3bdeea60b Correct some inconsistent markup in documentation.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2201 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-06-15 20:11:33 +00:00
cmpilato
3d8aecfd95 Tighten up some input validation.
* lib/viewvc.py
  (_re_validate_boolint): New.
  (_legal_params): Switch the validation function to _re_validate_boolint
   for the following: 'hideattic', 'makeimage', 'parent', 'tarball',
   and 'hidecvsroot'.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2199 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-06-15 19:41:30 +00:00
cmpilato
c713952d5e Minor documentation wording tweak.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2197 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-06-15 19:06:26 +00:00
svnuser
4b05147064 Bug 50473
A workaround for line counts when moving


git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@178 6955db30-a419-402b-8a0d-67ecbb4d7f56
2009-06-15 15:53:35 +00:00
cmpilato
8910c295cc Finish issue #420 - "cvsdbadmin rebuild" and "svndbadmin rebuild"
complain about unknown repositories.

* bin/svndbadmin
  (main): When rebuilding, ignore the UnknownRepositoryError; when
    purging, turn that sucker into something readable.

* bin/cvsdbadmin
  (__main__): When rebuilding, ignore the UnknownRepositoryError; when
    purging, turn that sucker into something readable.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2195 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-06-08 15:50:51 +00:00
cmpilato
1d496a8f51 Make error handling in cvsdb a little more flexible.
* lib/cvsdb.py
  (UnknownRepositoryError, DatabaseVersionError): New Exception classes.
  (CheckinDatabase.Connect): Raise DatabaseVersionError instead of a
    generic Exception.
  (CheckinDatabase.PurgeRepository): Raise UnknownRepositoryError
    instead of a generic Exception.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2194 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-06-08 15:23:16 +00:00
cmpilato
892a951493 Finish issue #419: svnauthz module doesn't match Subversion's
case-handling semantics.

* conf/viewvc.conf.dist
  (force_username_case): New option.

* lib/vcauth/svnauthz/__init__.py
  (ViewVCAuthorizer.__init__): Find and handle new force_username_case
    authorizer option.
  (ViewVCAuthorizer._get_paths_for_root): Replace ConfigParser.optionxform()
    (which does normalization of option names) with an identity function
    of sorts.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2192 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-06-05 19:03:07 +00:00
cmpilato
1371a71f20 Consistify the layout -- at least on a section-by-section basis -- of
the viewvc.conf.dist file.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2190 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-06-05 17:52:47 +00:00
cmpilato
aba3033c27 Remove CHANGES item for change that should appear in 1.1.2 instead of 1.2.0.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2189 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-06-04 15:43:55 +00:00
cmpilato
74bfd3e9d8 Post-release fun.
* CHANGES
  Copy 1.1.1's changes here.

* www/index.html
  Update 'latest release' notes.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2186 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-06-03 15:05:32 +00:00
cmpilato
7cb54076fe Finish (for now) the 1.1.0 release ntoes.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2183 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-05-29 18:16:41 +00:00
cmpilato
197f352804 * templates/file.ezt
Use "Contents of /path/to/file" instead of "Annotate of /path/to/file" as
  the page title.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2181 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-05-29 17:57:03 +00:00
cmpilato
f48559766a * lib/cvsdb.py
(CheckinDatabase.PurgeRepository): Don't allow the purge operation
    to actually *create* a "repositories" table entry.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2179 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-05-29 15:09:51 +00:00
cmpilato
546501816b Fix issue #417 - "cvsdbadmin rebuild" fails to record some of the
commit/repository info.

* lib/cvsdb.py
  (CheckinDatabase.PurgeRepository): Clear all the ID caches after
    running a purge operation so that, if the purge is actually part
    of a rebuild, the subsequent update doesn't merely pull IDs from
    the cache (with the side-effect of not readding them to the database).

Reported by: Naran Babhu <naranbabhu{_AT_}tigris.org
             Martin Dessureault <martin{_AT_}austin.rr.com>


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2178 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-05-29 15:08:32 +00:00
cmpilato
8089335fc6 Fix issue #416 - Can't see query form due to missing template variables.
* lib/viewvc.py
  (view_queryform): Add 'query_action' and 'query_hidden_values' back
    to the data dictionary, as they were accidentally dropped in r2123.

Reported by: Naran Babhu <naranbabhu{_AT_}tigris.org>


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2175 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-05-28 13:59:47 +00:00
svnuser
a208ec46ab Bug 37020
Search by revision number


git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@165 6955db30-a419-402b-8a0d-67ecbb4d7f56
2009-05-21 14:42:39 +00:00
cmpilato
d739127ea4 Update external to point to 1.2 versions of contributed templates.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2172 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-05-18 13:28:53 +00:00
cmpilato
4b4f450ecb Update CHANGES list for 1.1.0 release.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2169 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-05-13 17:11:27 +00:00
cmpilato
588f9000e9 Update the viewvc.org site.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2168 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-05-13 14:44:58 +00:00
cmpilato
7ecae1790a One last tweak.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2167 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-05-13 14:43:13 +00:00
cmpilato
a154179492 Need ... homepage ... beauty...
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2166 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-05-13 14:41:52 +00:00
cmpilato
cc4f8254d6 More Tigris homepage tweaks.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2165 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-05-13 14:40:24 +00:00
cmpilato
c695ede476 Update Tigris homepage.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2164 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-05-13 14:31:11 +00:00
cmpilato
c910df0835 Add left-nav link to source tarballs.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2163 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-05-13 14:20:53 +00:00
cmpilato
914e0e7521 Copy 1.0.8's CHANGES.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2152 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-05-05 17:26:04 +00:00
cmpilato
4c81b1fd91 Note new version of ViewVC.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2151 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-05-05 17:25:04 +00:00
cmpilato
92a95601d1 Bump copyright years.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2146 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-05-05 17:08:54 +00:00
cmpilato
211bc90343 Finish issue #402: Split the 'use_pagesize' configuration directive
into two: 'log_pagesize' and 'dir_pagesize', individually controlling
the size of pages used (if at all) for the revision log and directory
views, respectively.

* conf/viewvc.conf.dist
  (use_pagesize): Removed, in favor of...
  (dir_pagesize, log_pagesize): ...these.

* lib/viewvc.py,
* lib/config.py,
* bin/standalone.py
  Replace the use and tooling around 'use_pagesize' with
  'dir_pagesize' and 'log_pagesize' as appropriate.

* templates/include/paging.ezt,
* templates/include/dir_header.ezt
  Stop using the option value to determine whether or not to show
  pagination UI.  Use the presence of some pages instead.
  
* docs/upgrading-howto.html
  Note the changes in options.

Patch by: Lei Zhang <thestig{_AT_}google.com>
          (Tweaked by me.)


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2142 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-05-05 15:45:25 +00:00
cmpilato
fdff86c5f4 Fix the GUI mode of standalone.py -- it wasn't even starting
correctly, and we don't need a toggle for 'enscript' any more.

* bin/standalone.py
  (GUI.__init__): Pass the configuration file to handle_config().
    Lose 'enscript'-related stuff.
  (GUI.toggle_use_enscript): Remove.
  (cli): Fix call to gui(), dropping bogus parameter.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2140 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-05-05 15:34:11 +00:00
cmpilato
7ca7528869 Fix issue #409, an exception thrown when sorting by revision in a
remote Subversion directory view.

* lib/vclib/svn/svn_ra.py
  (RemoteSubversionRepository.dirlogs): Store the entry's revision as
    a string, for consistency with other vclib modules.

Reported by: Wojciech Wróblewski <wojtek{_AT_}elmi.pl>


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2137 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-05-05 14:52:35 +00:00
cmpilato
dfa98b490f Fix an exception in log views of Subversion repositories with 0
revisions.

* lib/vclib/svn/svn_repos.py
  (_get_history): Add easy-out for repositories with 0 revisions.

Reported by: Wojciech Wróblewski <wojtek{_AT_}elmi.pl>


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2134 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-05-04 14:53:24 +00:00
cmpilato
bacb71b1d2 Call make-release correctly.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2132 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-04-20 17:47:50 +00:00
cmpilato
3da11f485a Slight terminology tweak.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2129 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-04-20 17:40:51 +00:00
cmpilato
a8e6f976ef * tools/make-release
Make this able to build any branch, not just trunk and tag names.

* notes/releases.txt
  Update example of using make-release.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2128 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-04-20 17:38:57 +00:00
stas
86c313bcf8 Bug 37020
rss_href


git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@163 6955db30-a419-402b-8a0d-67ecbb4d7f56
2009-04-10 10:26:29 +00:00
vfilippov
a2e45b4468 Bug 37020
404 not found on log_href's of removed files
More request.roottype ---> my_repos['roottype']


git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@157 6955db30-a419-402b-8a0d-67ecbb4d7f56
2009-03-31 13:07:48 +00:00
stas
5ed8c4967a Bug 47903
Incorrect exam_rev for SVN deleted files


git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@150 6955db30-a419-402b-8a0d-67ecbb4d7f56
2009-03-25 18:14:54 +00:00
cmpilato
6d6a0287ac * lib/viewvc.py
(view_directory, view_log): Add some comments explaining the
    presence of certain data dictionary dummy values.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2124 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-03-24 17:01:53 +00:00
cmpilato
63af297920 Try to make ViewVC provide a consistent, predictable data dictionary
in all of its templated views.  Do this with a custom, dictionary-like
class that prevents the creation of new keys after an initial
instantiation of keys and (possibly dummy) values.

* lib/ezt.py
  (TemplateData): New.

* lib/viewvc.py
  (common_template_data): Now return an ezt.TemplateData() object.
  (make_comma_sep_list_string): New.
  (markup_or_annotate): Use new ezt.TemplateData() interface now.  Use
    make_comma_sep_list_string() where applicable.
  (view_roots, view_log, view_cvsgraph, view_diff, view_revision,
   view_queryform, view_query): Use new ezt.TemplateData() interface now.
  (view_directory): Use new ezt.TemplateData() interface now.  Drop
    'search_re_form' dictionary item.

* lib/query.py
  (main): Use new ezt.TemplateData() interface now.  While here, drop
    'script_name' calculation (it isn't used).

* templates/include/dir_header.ezt
  Display the search form if 'search_re_action' is set, not 'search_re_form'.

* docs/template-authoring-guide.html
  Remove 'search_re_form'.  Update 'search_re_action' description.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2123 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-03-24 16:55:25 +00:00
cmpilato
d7ac8ee2d4 * templates/include/file_header.ezt
Lose template reference of 'log_href_rev', which was removed in
  r2116 as unused.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2121 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-03-24 16:32:29 +00:00
cmpilato
11b4c4f4f8 Clutterkiller! Put the default configuration files into a conf/
subdirectory.

* conf/
  New subdirectory.

* cvsgraph.conf.dist,
* mimetypes.conf.dist,
* viewvc.conf.dist
  Move these from here ...

* conf/cvsgraph.conf.dist,
* conf/mimetypes.conf.dist,
* conf/viewvc.conf.dist
  ... to here.

* viewvc-install
  (FILE_INFO_LIST): Track new locations of configuration files.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2119 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-03-19 20:41:41 +00:00
cmpilato
c6671ecc50 Attempt to compensate for the fact that some versions of Subversion's
Python bindings do not correctly set the .apr_err and .message members
of SubversionException objects by patching up those exceptions using
the exception arguments.

(Unfortunately, I don't have a version of these bindings installed for
testing.)

* lib/vclib/svn/svn_repos.py
  (_fix_subversion_exception): New helper function.
  (_get_history, BlameSource.__init__, LocalSubversionRepository.rawdiff,
   LocalSubversionRepository.get_location, LocalSubversionRepository.last_rev):
    Use _fix_subversion_exception().

* lib/vclib/svn/svn_ra.py
  (): Import _fix_subversion_exception from svn_repos.
  (RemoteSubversionRepository._date_from_rev, 
   RemoteSubversionRepository.get_location): Use _fix_subversion_exception().


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2117 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-03-19 18:02:35 +00:00
cmpilato
0bcb42c158 * lib/viewvc.py
(common_template_data): Stop initializing an unused value.

* docs/template-authoring-guide.html
  Lose docs for unused (and spelled wrong, to boot) template variable.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2116 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-03-19 17:46:54 +00:00
cmpilato
586c6bfb9e * lib/ezt.py
(Template._cmd_for): Raise a more useful exception message when the
    to-be-looped-over variable ain't a sequence.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2115 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-03-19 17:44:07 +00:00
cmpilato
ca12b5257c * lib/ezt.py
(Template._cmd_print): Raise a more helpful exception to give some
    clues to template authors who tried to, say, print a sequence
    reference.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2111 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-03-19 15:21:30 +00:00
cmpilato
7fd9e405a1 Update copyright years.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2107 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-03-18 16:43:33 +00:00
cmpilato
6361a849a6 Update release notes.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2106 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-03-18 16:15:49 +00:00
cmpilato
e72184e288 Fix namespace problem with raised Exception.
* lib/viewvc.py
  (view_revision): raise *debug.*ViewVCException.

Patch by: Kamesh Jayachandran <kamesh@collab.net>


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2103 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-03-18 12:45:47 +00:00
cmpilato
2dc51276ce Finish issue #401: Support MIME type overrides in ViewVC configuration.
Trade the 'mime_types_file' option for 'mime_types_files' -- an
ordered list of MIME mapping files to consult.  And provide our own
(empty) mapping file that folks can use to override the mappings
provided by other such files.

* mimetypes.conf.dist
  New file.

* viewvc.conf.dist
  (mime_types_files): Was mime_types_file, and now accepts multiple values.

* lib/config.py
  (Config._force_multi_value): Add "mime_types_files" to the list of
    multi-value configuration options.
  (Config.set_defaults): Track rename of mime_types_file parameter,
    now setting the default to a list containing only "mimetypes.conf".

* lib/viewvc.py
  (load_config): Track new name and format of mime_types_files option.

* viewvc-install
  (FILE_INFO_LIST): Also install mimetypes.conf.dist as itself and as
    mimetypes.conf.

* INSTALL
  (INSTALLING VIEWVC): Update reference to renamed configuration option.

* docs/upgrading-howto.html
  Update this document.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2101 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-03-06 16:43:04 +00:00
cmpilato
e1575692be Finish issue #396 - Malformed accept-language header crashes viewvc
* lib/accept.py
  (AcceptLanguageParseError): Was AcceptParseError.
  (_parse): Track renamed Exception.

* lib/viewvc.py
  (Request.__init__): Catch and handle raised AcceptLanguageParseError.

Patch by: Rune Halvorsen <runefh{_AT_}gmail.com>,
          me


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2097 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-02-26 16:12:15 +00:00
cmpilato
748efe3815 Add FAQ entry about exposing ViewVC at a vhost root.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2096 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-02-24 13:36:41 +00:00
cmpilato
d681df85d3 Allow ViewVC to (optionally) use the 'chardet' module during syntax
coloration to perform character encoding detection (and subsequent
translation to UTF-8).

(Thanks to Jeremy Whitlock <jcscoobyrs@gmail.com> for pointing me in
this direction, if inadvertantly.)

* viewvc.conf.dist
  (options.detect_encoding): New.

* lib/config.py
  (Config.set_defaults): Initialize the 'detect_encoding' parameter.

* lib/viewvc.py
  (markup_stream_pygments): If the configuration asks for character
    encoding detection, try to import the 'chardet' module and -- if all
    goes well -- tell Pygments to use it.

* CHANGES
  Note this change.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2095 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-02-20 17:23:27 +00:00
cmpilato
e53b3af42f ViewVC doesn't "do" directory entry sorting by revision for CVS, so
don't imply that it does in the UI.

* lib/viewvc.py
  (view_directory): Don't provide sortby_rev_href to the template.

* templates/directory.ezt
  Only offer sort links when those links are provided by ViewVC.

* templates/dir_new.ezt
  Only offer sort links when those links are provided by ViewVC.  Use
  date-based sorting if rev-based sorting isn't available.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2090 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-02-20 15:20:15 +00:00
stas
dbd3e4af59 Bug 46528
Bug 46710
default format


git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@134 6955db30-a419-402b-8a0d-67ecbb4d7f56
2009-02-16 12:57:47 +00:00
vfilippov
5349a36a18 Bug 46528
Bug 46710
OR in group formats


git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@133 6955db30-a419-402b-8a0d-67ecbb4d7f56
2009-02-16 12:55:32 +00:00
stas
81df47c357 Bug 46528
strip's


git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@132 6955db30-a419-402b-8a0d-67ecbb4d7f56
2009-02-16 12:44:38 +00:00
stas
c916450eac Bug 46710
Bug 46528
Debugw


git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@131 6955db30-a419-402b-8a0d-67ecbb4d7f56
2009-02-16 11:43:17 +00:00
stas
e4ea3a9f85 Bug 46710
Bug 46528
debug cvsntacl authorizer


git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@130 6955db30-a419-402b-8a0d-67ecbb4d7f56
2009-02-16 11:41:16 +00:00
vfilippov
e6be979387 Bug 46528
By root group check specification


git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@128 6955db30-a419-402b-8a0d-67ecbb4d7f56
2009-02-13 10:43:39 +00:00
vfilippov
2896c70f26 Bug 46528
New authorizers: grp, union, cvsntacl


git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@127 6955db30-a419-402b-8a0d-67ecbb4d7f56
2009-02-12 16:37:15 +00:00
vfilippov
50b67b3ddb Bug 45076
git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@126 6955db30-a419-402b-8a0d-67ecbb4d7f56
2009-02-11 12:57:01 +00:00
vfilippov
3c7ad9405c Bug 46239
Look in CVS/SVN/All repos link on query results page


git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@121 6955db30-a419-402b-8a0d-67ecbb4d7f56
2009-02-03 12:19:33 +00:00
cmpilato
d1a7412c6d Remove dead code.
* lib/popen.py
  (pipe_cmds, _copy): Remove as unused.

* bin/standalone.py
  (StandaloneServer.run_viewvc): Tweak comment that referred to pipe_cmds().

Found by: Rune Halvorsen <runeh {AT} sanedefaults.net>


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2088 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-02-03 03:53:25 +00:00
cmpilato
ee692db2b0 Fix issue #398 (File Log Viewer Shows Wrong Log Message) by asking the
vclib layer for revision-sorted output rather than "whatever you want
to give me".

* lib/viewvc.py
  (markup_or_annotate): Pass vclib.SORTBY_REV instead of vclib.SORTBY_DEFAULT
    to the itemlog() interface.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2086 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-02-02 18:10:59 +00:00
vfilippov
ab286694fe Bug 46239
Search only CVS/SVN repositories option


git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@119 6955db30-a419-402b-8a0d-67ecbb4d7f56
2009-02-02 17:20:11 +00:00
vfilippov
1cf0815b3d Bug 45675
Unsecure patch detection fix for SVN roots


git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@108 6955db30-a419-402b-8a0d-67ecbb4d7f56
2009-01-27 11:27:05 +00:00
cmpilato
544ca74ed9 Add a skeletal version of some semblance of 1.1.0 release notes, taken
from an email response I gave on the dev@ list.  Will massage this
into something a little more ready for publicity later.

* docs/release-notes,
* docs/release-notes/1.1.0.html
  New.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2085 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-01-15 16:56:44 +00:00
vfilippov
355033b015 Bug 45675 -- debug
!!previous commit also was related to bug 45675


git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@100 6955db30-a419-402b-8a0d-67ecbb4d7f56
2009-01-15 10:18:33 +00:00
vfilippov
2f99f7b72c Bug 45675 -- better way of diffing added/removed files
git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@99 6955db30-a419-402b-8a0d-67ecbb4d7f56
2009-01-15 10:00:38 +00:00
vfilippov
e8d7fb16ff Bug 45675
debug


git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@98 6955db30-a419-402b-8a0d-67ecbb4d7f56
2009-01-15 09:11:40 +00:00
vfilippov
e1cc47c375 Bug 45675: Patches from query results
git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@97 6955db30-a419-402b-8a0d-67ecbb4d7f56
2009-01-14 14:07:41 +00:00
cmpilato
f6844dda91 Finish issue #334 by adding an empty CSS class def and some references
to it for line number columns in the diff view.

* templates/docroot/styles.css
  (.vc_diff_line_number): New.

* templates/diff.ezt
  Use new vc_diff_line_number class.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2083 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-01-13 19:10:09 +00:00
cmpilato
5fdfefa137 Document the new database schema stuff.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2081 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-01-13 18:44:43 +00:00
cmpilato
cd64b5da8b Add some real command-line parameter handling to 'make-database',
effectively allowing for non-interactive operation.

* bin/make-database
  (INTRO_TEXT): Removed, merged into the usage message printed by...
  (usage_and_exit): ...this new function.
  (__main__): Add new command-line parsing logic, including support of
    new options (--username, --hostname, --password, --dbname) and the
    replacement of the --bonsai-compatible option with a more generic
    --version.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2079 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-01-13 18:18:46 +00:00
cmpilato
623ab85805 Merge to trunk all changes made on the issue-366-dev branch,
completing issue #366 (cvsdb purge operation is painfully slow).

* lib/cvsdb.py,
* bin/make-database


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2078 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-01-13 18:16:11 +00:00
cmpilato
ceb7057b9f Remove unnecessary empty svn:mergeinfo.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2077 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-01-13 18:14:52 +00:00
stas
5a55d3c0cd O_o max context value = 2^25-1 ... workaround for Bug 45200
git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@76 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-12-22 18:13:08 +00:00
vfilippov
00d1ffe3e0 fix
git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@75 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-12-22 18:00:37 +00:00
cmpilato
0b1ac97f75 Try to deal with Subversion versions that lack the
SVN_ERR_CEASE_INVOCATION error code.

* lib/vclib/svn/svn_repos.py
  (_SVN_ERR_CEASE_INVOCATION): New compatibility variable.
  (NodeHistory.add_history, _get_history): Use compatibility variable
    instead of testing for SVN_ERR_CEASE_INVOCATION directly.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2070 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-12-19 19:48:43 +00:00
cmpilato
3e77755819 Document new num_changes item in upgrade notes.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2068 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-12-19 19:45:44 +00:00
cmpilato
1433941124 Expose to revision.ezt a count of the total number of changed paths.
* lib/viewvc.py
  (view_revision): Add new 'num_changes' data dictionary item.

* templates/revision.ezt
  Expose new 'num_changes' item as a header on this page, and tweak
  the way the more/first changes links appear.

* docs/template-authoring-guide.html
  Document num_changes dictionary item.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2067 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-12-19 19:43:18 +00:00
vfilippov
0d3176a320 bug 45200
'rcsdiff' workaround for fucking cvsnt


git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@73 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-12-19 14:47:24 +00:00
vfilippov
053cc33ffd bug 44943
blame_source ---->> utf8string


git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@68 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-12-16 14:29:30 +00:00
vfilippov
93144b2168 bug 37020
bug 44996
more CVS charset fixes


git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@62 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-12-12 16:19:41 +00:00
vfilippov
c102b6b2a0 utf8string cvs logs
git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@61 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-12-12 12:36:39 +00:00
vfilippov
7cd0e372bb russian filenames in cvs fix
git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@60 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-12-11 16:02:02 +00:00
vfilippov
4e047ae281 bug 44931
s.encode('utf-8') in ezt


git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@59 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-12-11 10:46:42 +00:00
cmpilato
c733fceb9f Rework the 'rlog-ended-early' FAQ bit with a more generic entry.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2066 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-12-10 18:12:29 +00:00
cmpilato
ca5a2bfbc9 Fix issue #385 (Hide/show annotations links mismanaged for binary
files).

* lib/viewvc.py
  (markup_or_annotate): Make the default value for the 'annotation'
    data dictionary member 'none' (instead of None) to simplify
    template logic.

* docs/template-authoring-guide.html
  Update docs for 'annotated'.

* templates/file.ezt
  Use simplified logic to avoid missing links.  (No, not *that* kind ...)


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2064 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-12-10 17:26:37 +00:00
cmpilato
77fd2759c7 Remove unnecessary module imports.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2063 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-12-10 17:25:11 +00:00
vfilippov
1128625ac3 bug 44887
debug Magic MIME type guessing


git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@58 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-12-10 13:21:37 +00:00
stas
15e2d27b7d xml
git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@57 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-12-10 13:06:59 +00:00
vfilippov
86dcf849e3 debug magic code
git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@56 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-12-10 13:03:55 +00:00
vfilippov
5f66346633 bug 44887
do not annotate binary files


git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@55 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-12-10 12:26:16 +00:00
vfilippov
6586c8e4d7 bug 37020
enc fix


git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@54 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-12-09 18:44:05 +00:00
vfilippov
d34f8f5946 bug 37020
debug


git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@53 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-12-09 16:52:16 +00:00
vfilippov
842f0e712a bug 37020
templates for multi-repos query form


git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@52 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-12-09 16:46:42 +00:00
vfilippov
0ad8ee1554 debug
git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@51 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-12-09 16:46:28 +00:00
vfilippov
0f545ca76b del
git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@50 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-12-09 16:36:37 +00:00
vfilippov
c3f2777a4e bug 37020
multi-root query form


git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@49 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-12-09 16:34:49 +00:00
vfilippov
45c636fd11 bug 37020
no pagesize by default


git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@48 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-12-09 15:18:31 +00:00
vfilippov
2d9276a06d KEY descid
git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@47 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-12-09 14:54:40 +00:00
vfilippov
eb6edef712 cvs logs -----> utf8
git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@46 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-12-09 11:23:21 +00:00
vfilippov
b574b8594d utf8
git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@45 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-12-08 13:00:51 +00:00
vfilippov
3bfb004158 asynchronous SVN update script
git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@44 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-12-08 12:32:42 +00:00
cmpilato
08a8b110ac Allow admins to enable/disable diff/patch views via the allowed_views
configuration bit.

* lib/viewvc.py
  (view_diff, view_patch): Only show diffs if 'diff' is one of the
    allowed views.

* lib/config.py
  (Config.set_defaults): Add 'diff' to the set of allowed_views (and
    sort the values alphabetically where here).

* viewvc.conf.dist
  (allowed_views): Add 'diff' to the set of views you can specify, and
    include it in the default values.  While here, sort the values
    alphabetically.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2058 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-12-05 17:01:39 +00:00
vfilippov
23eea873fa fix a bug in setup cvs hooks script
git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@43 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-12-04 21:51:19 +00:00
vfilippov
9f0557ff85 testing ViewVC hooks
git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@42 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-12-04 21:32:44 +00:00
vfilippov
00f38a1b55 utf8string diffs
git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@41 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-12-04 15:47:45 +00:00
vfilippov
3b4f7698af rights
git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@40 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-12-02 14:55:27 +00:00
vfilippov
0114b46db0 fix directory rights
git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@39 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-12-02 14:53:23 +00:00
vfilippov
a0c895b5a6 debug
git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@38 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-12-02 14:46:25 +00:00
vfilippov
e74a8ac42d Apache config via virthost
git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@37 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-12-02 14:15:01 +00:00
vfilippov
d94e658457 cvs_ondisk_charset
git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@36 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-12-02 10:39:18 +00:00
vfilippov
cbcffad79a physical charset
git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@35 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-12-01 18:16:46 +00:00
vfilippov
025eb36df4 multiple repos co CVSROOT
git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@34 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-11-27 15:20:35 +00:00
vfilippov
33a8f2849c CustIS install script, it's done and tested now
git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@33 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-11-26 14:18:40 +00:00
vfilippov
1bbf731b83 setup-svn-hooks
git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@32 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-11-26 14:15:32 +00:00
vfilippov
cd68fb0f79 improved ACL import script
git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@31 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-11-21 17:06:03 +00:00
vfilippov
97e5d91c18 import
git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@30 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-11-18 17:25:26 +00:00
vfilippov
28ba6929af cvsacl
git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@29 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-11-18 17:24:50 +00:00
vfilippov
3187e57026 None
git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@28 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-11-18 13:49:12 +00:00
vfilippov
a30b597260 check access rights when querying commit database
git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@27 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-11-18 13:36:33 +00:00
vfilippov
1480000938 debug
git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@26 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-11-17 17:04:16 +00:00
vfilippov
bfe175e5cd setup cvs hook script
git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@25 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-11-17 16:19:31 +00:00
vfilippov
2cadb542a2 pygments
git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@24 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-11-14 19:42:47 +00:00
vfilippov
13b23a5696 total plus/minus lines
git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@23 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-11-14 17:57:57 +00:00
vfilippov
822ec29624 bug 37020
fucking encodings


git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@22 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-11-14 14:53:48 +00:00
cmpilato
38a7338c9d Add a configuration option for telling ViewVC to *not* honor the
svn:mime-type property.

* lib/config.py
  (Config.set_defaults): Set options.svn_ignore_mimetype default value.

* viewvc.conf.dist
  (svn_ignore_mimetype): New.

* lib/viewvc.py
  (calculate_mime_type): Consult cfg.options.svn_ignore_mimetype
    before looking up the 'svn:mime-type' property.

* docs/upgrading-howto.html
  Add a note about the new configuration option.

Patch (mostly) by: JJ <eggsgloriouseggs {_AT_} gmail.com>


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2055 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-11-13 21:44:15 +00:00
cmpilato
bf40ef2683 It's very confusing to comment out the authorizer configuration line
and have that mean "use an authorizer".  So let's just prevent that
confusion right now, okay?

* lib/config.py
  (Config.set_defaults): Set 'authorizer' by default to None.

* viewvc.conf.dist
  (authorizer): Update comments for correctness, and don't imply that
    "forbidden" is the default authorizer.

* docs/upgrading-howto.html
  Stop saying that "forbidden" is the default authorizer.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2053 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-11-13 21:20:29 +00:00
cmpilato
11f5685955 Finish issue #384 - repos._revinfo_raw() is doing far too much work
when no authz is in use.  Vastly improves performance of
repos.itemlog() for local Subversion repositories *not* protected by
an authorizer.

* lib/vclib/svn/svn_repos.py
  (LocalSubversionRepository._revinfo): Was ._revinfo_raw().  Now
    encapsulates the caching logic that used to live in .revinfo(), plus
    the old logic of _revinfo_raw() modified to avoid changed-path
    processing when that processing isn't strictly required by callers.
  (LocalSubversionRepository.revinfo): Make this just a thin wrapper
    around ._revinfo() now.  Internal callers of this function updated
    to use ._revinfo() instead.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2051 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-11-13 21:14:14 +00:00
cmpilato
4904e03324 Minor improvements to the debug timer code.
* lib/debug.py
  (t_dump): Was dump().  Now accepts an output file handle, and sorts
    the output in a predictable fashion.

* lib/viewvc.py
  (main): Track changes to debug.dump().

git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2048 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-11-13 19:45:13 +00:00
vfilippov
f9b1b8a50c encodings
git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@21 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-11-13 16:51:57 +00:00
vfilippov
6978ac9e5d merged
git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@20 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-11-13 14:56:58 +00:00
vfilippov
230e2f1700 bug
git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@19 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-11-12 20:17:34 +00:00
vfilippov
899204468b cron reload
git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@18 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-11-12 19:04:57 +00:00
vfilippov
712761df3f fast cvsdbadmin
git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@17 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-11-12 18:52:54 +00:00
vfilippov
73eaad686b bug 37020
git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@16 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-11-12 18:48:25 +00:00
vfilippov
6556287ff3 bug 37020
debug


git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@15 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-11-12 18:46:44 +00:00
vfilippov
4b51a62390 bug 37020
git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@14 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-11-12 18:45:01 +00:00
vfilippov
5ea538f50e Pygments from tarball
git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@13 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-11-12 18:26:03 +00:00
vfilippov
521261bb34 bug 37020
automatic hook setup


git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@12 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-11-12 18:14:50 +00:00
vfilippov
9e55dba9f9 bug 37020
git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@11 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-11-12 17:40:19 +00:00
vfilippov
9ac8f10967 bug 37020
http_proxy


git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@10 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-11-12 17:08:12 +00:00
vfilippov
6ef2a85ae9 debug
git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@9 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-11-12 16:42:44 +00:00
vfilippov
1200a15887 bug 37020
config


git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@8 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-11-12 16:39:48 +00:00
vfilippov
0fddc0bb87 Bug 37020
Install script


git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@7 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-11-12 16:12:45 +00:00
vfilippov
28a41fb268 fix pygments
git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@6 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-11-12 16:12:19 +00:00
cmpilato
bd1254aa5f Avoid returning huge lists of variables from get_file_view_info(), and
use an ad-hoc class to hold all that stuff instead.

* lib/viewvc.py
  (get_file_view_info): Now return an object with interesting member variables
    instead of a nasty long list of values in an easy-to-goof-up order.
  (common_template_data, view_directory, view_log, view_diff): Update
    uses of get_file_view_info to track new calling semantics.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2047 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-11-11 16:21:58 +00:00
vfilippov
585580ec57 bug 37020
Ability to connect to MySQL through UNIX socket
diff links in query.py are shown now (moved expand_root_parents to config.py)
full-text comment searches in query and query_form are supported using MySQL FULLTEXT indexes
<select> for repository selection in query


git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@5 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-11-11 14:25:07 +00:00
vfilippov
f3aa325419 bug 37020
viewvc 1.1.0-beta1 initial commit


git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@4 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-11-11 14:17:41 +00:00
vfilippov
9b00bc278f bug 43834
dirs


git-svn-id: svn://svn.office.custis.ru/3rdparty/viewvc.org/trunk@3 6955db30-a419-402b-8a0d-67ecbb4d7f56
2008-11-11 14:12:33 +00:00
cmpilato
8b6e568ae1 * viewvc.org/faq.html: Minor wording tweak.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2046 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-11-10 22:05:27 +00:00
cmpilato
bc13ce9938 * viewvc.org/faq.html: Update the answer to the authz question.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2045 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-11-10 14:29:47 +00:00
cmpilato
0ddf057bbc Minor website tweakage.
* viewvc.org/index.html
  Lose 'Future Plans' section, and overhaul the feature and requirements lists.

* viewvc.org/contributing.html
  Fix the link to the Template Authoring Guide.

* viewvc.org/download.html
  Add a section for upgrading existing ViewVC installations.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2044 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-11-06 20:36:37 +00:00
cmpilato
5dbdea76ce docs/upgrading-howto.html: Don't claim as removed options not in 1.0.x.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2043 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-11-05 21:13:54 +00:00
cmpilato
8a9b9562f3 Merge r2040 from the 1.1.x branch:
* CHANGES: Combine some changes, and remove a now-bogus one.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2041 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-11-05 16:31:47 +00:00
cmpilato
51994aabc9 Doh! Remove empty <div> section.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2039 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-11-04 21:45:28 +00:00
cmpilato
f3edee505d Website and CHANGES file updates.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2038 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-11-04 21:44:44 +00:00
cmpilato
bcfb8f2c97 * lib/viewvc.py (__version__): Bump to '1.2-dev'.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2034 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-10-30 17:37:04 +00:00
241 changed files with 10951 additions and 7674 deletions

92
CHANGES
View File

@@ -1,71 +1,4 @@
Version 1.1.7 (released 09-Sep-2010)
* display Subversion revision properties in the revision view (issue #453)
* fix exception in 'standalone.py -r REPOS' when run without a config file
* fix standalone.py server root deployments (--script-alias='')
* add rudimentary Basic authentication support to standalone.py (issue #49)
* fix obscure "unexpected NULL parent pool" Subversion bindings error
* enable path info / link display in remote Subversion root revision view
* fix vhost name case handling inconsistency (issue #466)
* use svn:mime-type property charset param as encoding hint
* markup Subversion revision references in log messages (issue #313)
* add rudimentary support for FastCGI-based deployments (issue #464)
* fix query script WSGI deployment
* add configuration to fix query script cross-linking to ViewVC
Version 1.1.6 (released 02-Jun-2010)
* add rudimentary support for WSGI-based deployments (issue #397)
* fix exception caused by trying to HTML-escape non-string data (issue #454)
* fix incorrect RSS feed Content-Type header (issue #449)
* fix RSS <title> encoding problem (issue #451)
* allow 'svndbadmin purge' to work on missing repositories (issue #452)
Version 1.1.5 (released 29-Mar-2010)
* security fix: escape user-provided search_re input to avoid XSS attack
Version 1.1.4 (released 10-Mar-2010)
* security fix: escape user-provided query form input to avoid XSS attack
* fix standalone.py failure (when per-root options aren't used) (issue #445)
* fix annotate failure caused by ignored svn_config_dir (issue #447)
Version 1.1.3 (released 22-Dec-2009)
* security fix: add root listing support of per-root authz config
* security fix: query.py requires 'forbidden' authorizer (or none) in config
* fix URL-ification of truncated log messages (issue #3)
* fix regexp input validation (issue #426, #427, #440)
* add support for configurable tab-to-spaces conversion
* fix not-a-sequence error in diff view
* allow viewvc-install to work when templates-contrib is absent
* minor template improvements/corrections
* expose revision metadata in diff view (issue #431)
* markup file/directory item property URLs and email addresses (issue #434)
* make ViewVC cross copies in Subversion history by default
* fix bug that caused standalone.py failure under Python 1.5.2 (issue #442)
* fix support for per-vhost overrides of authorizer parameters (issue #411)
* fix root name identification in query.py interface
Version 1.1.2 (released 11-Aug-2009)
* security fix: validate the 'view' parameter to avoid XSS attack
* security fix: avoid printing illegal parameter names and values
* add optional support for character encoding detection (issue #400)
* fix username case handling in svnauthz module (issue #419)
* fix cvsdbadmin/svnadmin rebuild error on missing repos (issue #420)
* don't drop leading blank lines from colorized file contents (issue #422)
* add file.ezt template logic for optionally hiding binary file contents
Version 1.1.1 (released 03-Jun-2009)
* fix broken query form (missing required template variables) (issue #416)
* fix bug in cvsdb which caused rebuild operations to lose data (issue #417)
* fix cvsdb purge/rebuild repos lookup to error on missing repos
* fix misleading file contents view page title
Version 1.1.0 (released 13-May-2009)
Version 1.1.0 (released ??-???-????)
* add support for full content diffs (issue #153)
* make many more data dictionary items available to all views
@@ -79,7 +12,9 @@ Version 1.1.0 (released 13-May-2009)
* add support for query by log message (issues #22, #121)
* fix bug parsing 'svn blame' output with too-long author names (issue #221)
* fix default standalone.py port to be within private IANA range (issue #234)
* add unified configury of allowed views; checkout view disabled by default
* add support for integration with GNU source-highlight (issue #285)
* add unified configury of allowed views
* 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 (issue #371)
@@ -101,24 +36,7 @@ Version 1.1.0 (released 13-May-2009)
* 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
* allow hiding of errorful entries from the directory view (issue #105)
* fix directory view sorting UI
* tolerate malformed Accept-Language headers (issue #396)
* allow MIME type mapping overrides in ViewVC configuration (issue #401)
* fix exception in rev-sorted remote Subversion directory views (issue #409)
* allow setting of page sizes for log and dir views individually (issue #402)
Version 1.0.9 (released 11-Aug-2009)
* security fix: validate the 'view' parameter to avoid XSS attack
* security fix: avoid printing illegal parameter names and values
Version 1.0.8 (released 05-May-2009)
* fix directory view sorting UI
* tolerate malformed Accept-Language headers (issue #396)
* fix directory log views in revision-less Subversion repositories
* fix exception in rev-sorted remote Subversion directory views (issue #409)
* add support for hiding errorful entries from the directory view (issue #105)
Version 1.0.7 (released 14-Oct-2008)

119
INSTALL
View File

@@ -139,7 +139,7 @@ installation instructions.
default_root
root_as_url_component
rcs_dir
mime_types_files
mime_types_file
There are some other options that are usually nice to change. See
viewvc.conf for more information. ViewVC provides a working,
@@ -168,23 +168,14 @@ checkin database working are below.
APACHE CONFIGURATION
--------------------
1) Locate your Apache configuration file(s).
1) Find out where the web server configuration file is kept. Typical
locations are /etc/httpd/httpd.conf, /etc/httpd/conf/httpd.conf,
and /etc/apache/httpd.conf. Depending on how apache was installed,
you may also look under /usr/local/etc or /etc/local. Use the vendor
documentation or the find utility if in doubt.
Typical locations are /etc/httpd/httpd.conf,
/etc/httpd/conf/httpd.conf, and /etc/apache/httpd.conf. Depending
on how Apache was installed, you may also look under /usr/local/etc
or /etc/local. Use the vendor documentation or the find utility if
in doubt.
2) Configure Apache to expose ViewVC to users at the URL of your choice.
ViewVC provides several different ways to do this. Choose one of
the following methods:
-----------------------------------
METHOD A: CGI mode via ScriptAlias
-----------------------------------
The ScriptAlias directive is very useful for pointing
Either METHOD A:
2) The ScriptAlias directive is very useful for pointing
directly to the viewvc.cgi script. Simply insert a line containing
ScriptAlias /viewvc <VIEWVC_INSTALLATION_DIRECTORY>/bin/cgi/viewvc.cgi
@@ -195,34 +186,34 @@ APACHE CONFIGURATION
ScriptAlias /viewvc /usr/local/viewvc-1.0/bin/cgi/viewvc.cgi
ScriptAlias /query /usr/local/viewvc-1.0/bin/cgi/query.cgi
----------------------------------------
METHOD B: CGI mode in cgi-bin directory
----------------------------------------
Copy the CGI scripts from
continue with step 3).
or alternatively METHOD B:
2) Copy the CGI scripts from
<VIEWVC_INSTALLATION_DIRECTORY>/bin/cgi/*.cgi
to the /cgi-bin/ directory configured in your httpd.conf file.
continue with step 3).
------------------------------------------
METHOD C: CGI mode in ExecCGI'd directory
------------------------------------------
Copy the CGI scripts from
and then there's METHOD C:
2) Copy the CGI scripts from
<VIEWVC_INSTALLATION_DIRECTORY>/bin/cgi/*.cgi
to the directory of your choosing in the Document Root adding the following
Apache directives for the directory in httpd.conf or an .htaccess file:
apache directives for the directory in httpd.conf or an .htaccess file:
Options +ExecCGI
AddHandler cgi-script .cgi
Note: For this to work mod_cgi has to be loaded. And for the .htaccess file
(Note: For this to work mod_cgi has to be loaded. And for the .htaccess file
to be effective, "AllowOverride All" or "AllowOverride Options FileInfo"
needs to have been specified for the directory.
need to have been specified for the directory.)
------------------------------------------
METHOD D: Using mod_python (if installed)
------------------------------------------
Copy the Python scripts and .htaccess file from
continue with step 3).
or if you've got Mod_Python installed you can use METHOD D:
2) Copy the Python scripts and .htaccess file from
<VIEWVC_INSTALLATION_DIRECTORY>/bin/mod_python/
to a directory being served by Apache.
to a directory being served by apache.
In httpd.conf, make sure that "AllowOverride All" or at least
"AllowOverride FileInfo Options" are enabled for the directory
@@ -232,63 +223,17 @@ APACHE CONFIGURATION
feature may not work because it uses multithreading. This works fine
under Apache 2.
----------------------------------------
METHOD E: Using mod_wsgi (if installed)
----------------------------------------
Copy the Python scripts file from
<VIEWVC_INSTALLATION_DIRECTORY>/bin/mod_python/
to the directory of your choosing. Modify httpd.conf with the
following directives:
continue with step 3).
WSGIScriptAlias /viewvc <VIEWVC_INSTALLATION_DIRECTORY>/bin/wsgi/viewvc.wsgi
WSGIScriptAlias /query <VIEWVC_INSTALLATION_DIRECTORY>/bin/wsgi/query.wsgi
3) Restart apache. The commands to do this vary. "httpd -k restart" and
"apache -k restart" are two common variants. On RedHat Linux it is
done using the command "/sbin/service httpd restart" and on SuSE Linux
it is done with "rcapache restart"
You'll probably also need the following directive because of the
not-quite-sanctioned way that ViewVC manipulates Python objects.
4) Optional: Add access control.
WSGIApplicationGroup %{GLOBAL}
Note: WSGI support in ViewVC is at this time quite rudimentary,
bordering on downright experimental. Your mileage may vary.
-----------------------------------------
METHOD F: Using mod_fcgid (if installed)
-----------------------------------------
This uses ViewVC's WSGI support (from above), but supports using FastCGI,
and is a somewhat hybrid approach of several of the above methods.
Especially if fcgi is already being used for other purposes, e.g. PHP,
also using fcgi can prevent the need for including additional modules
(e.g. mod_python or mod_wsgi) within Apache, which may help lessen Apache's
memory usage and/or help improve performance.
This depends on mod_fcgid:
http://httpd.apache.org/mod_fcgid/
as well as the fcgi server from Python's flup package:
http://pypi.python.org/pypi/flup
http://trac.saddi.com/flup
The following are some example httpd.conf fragments you can use to
support this configuration:
ScriptAlias /viewvc /usr/local/viewvc/bin/wsgi/viewvc.fcgi
ScriptAlias /query /usr/local/viewvc/bin/wsgi/query.fcgi
3) Restart Apache.
The commands to do this vary. "httpd -k restart" and "apache -k
restart" are two common variants. On RedHat Linux it is done using
the command "/sbin/service httpd restart" and on SuSE Linux it is
done with "rcapache restart". Other systems use "apachectl restart".
4) [Optional] Add access control.
In your httpd.conf you can control access to certain modules by
adding directives like this:
In your httpd.conf you can control access to certain modules by adding
directives like this:
<Location "<url to viewvc.cgi>/<modname_you_wish_to_access_ctl>">
AllowOverride None

View File

@@ -15,7 +15,7 @@
<blockquote>
<p><strong>Copyright &copy; 1999-2010 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
@@ -59,8 +59,6 @@
<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>
<li>March 18, 2009 &mdash; copyright years updated</li>
<li>March 29, 2010 &mdash; copyright years updated</li>
</ul>
</body>

1
README
View File

@@ -4,3 +4,4 @@ Please read the file INSTALL for more information.
And see windows/README for more information on running ViewVC on
Microsoft Windows.

View File

@@ -54,10 +54,7 @@ import query
server = sapi.AspServer(Server, Request, Response, Application)
try:
cfg = viewvc.load_config(CONF_PATHNAME, server)
viewvc_base_url = cfg.query.viewvc_base_url
if viewvc_base_url is None:
viewvc_base_url = "viewvc.asp"
query.main(server, cfg, viewvc_base_url)
query.main(server, cfg, "viewvc.asp")
finally:
s.close()

View File

@@ -54,7 +54,4 @@ import query
server = sapi.CgiServer()
cfg = viewvc.load_config(CONF_PATHNAME, server)
viewvc_base_url = cfg.query.viewvc_base_url
if viewvc_base_url is None:
viewvc_base_url = "viewvc.cgi"
query.main(server, cfg, viewvc_base_url)
query.main(server, cfg, "viewvc.cgi")

216
bin/custis-install Executable file
View File

@@ -0,0 +1,216 @@
#!/bin/sh
# ViewVC installation script for CustIS
if [ -f custis-install-config ]; then
. custis-install-config
else
cat >custis-install-config <<EOF
CVS_ASYNC=
SVN_ASYNC=
CVS_USER=www-data
CVS_GROUP=cvs.users
CVSROOTS=
SVNROOT=
SVN_FORBIDDENRE=
DB_HOST=
DB_PORT=
DB_SOCKET=/var/run/mysqld/mysqld.sock
DB=
DB_USER=
DB_PASSWD=
VIEWVC_DIR=
VIEWVC_URI=/
VIEWVC_URI_SLASH=/
VIEWVC_STATIC_URI=/static
http_proxy=
no_proxy=
EOF
echo Empty 'custis-install-config' initialized, please edit it before installation.
exit
fi
if [ ! "$DB" -o ! "$VIEWVC_DIR" ]; then
echo Please set up 'custis-install-config' before installation.
exit
fi
################################################################################
export http_proxy
export no_proxy
DEPS="python libapache2-mod-python rcs diff cvsnt subversion subversion-tools python-setuptools python-subversion python-mysqldb cvsgraph"
echo "*** Installing dependency packages: $DEPS"
apt-get install $DEPS
echo "*** Installing Pygments for Python"
wget http://pypi.python.org/packages/source/P/Pygments/Pygments-0.11.1.tar.gz
tar -zxf Pygments-0.11.1.tar.gz
cd Pygments-0.11.1
python setup.py install
cd ..
echo "*** Installing ViewVC into $VIEWVC_DIR"
if [ ! -e $VIEWVC_DIR/viewvc.conf ]; then
../viewvc-install <<EOF
$VIEWVC_DIR
EOF
fi
echo "*** Writing ViewVC configuration into $VIEWVC_DIR/viewvc.conf"
for CVSROOT in $CVSROOTS; do
if [ "$vccvsroots" ]; then
vccvsroots="$vccvsroots, "
fi
vcrootname=`basename $CVSROOT`
vccvsroots="$vccvsroots$vcrootname: $CVSROOT"
done;
cat >"$VIEWVC_DIR/viewvc.conf" <<EOF
[general]
cvs_roots = $vccvsroots
root_parents = $SVNROOT : svn
cvsnt_exe_path = /usr/bin/cvsnt
mime_types_file = /etc/mime.types
address = Admin address: stas [gav-gav] custis [ru]
kv_files =
languages = en-us, ru-ru
[utilities]
rcs_dir = /usr/bin
cvsnt = /usr/bin/cvsnt
svn = /usr/bin/svn
diff = /usr/bin/diff
cvsgraph = /usr/bin/cvsgraph
[options]
allowed_views = markup, annotate, roots
authorizer = forbiddenre
checkout_magic = 0
cross_copies = 1
cvsgraph_conf = $VIEWVC_DIR/cvsgraph.conf
default_file_view = log
diff_format = h
docroot = $VIEWVC_STATIC_URI
enable_syntax_coloration = 1
generate_etags = 1
hide_attic = 1
hide_cvsroot = 1
hide_errorful_entries = 0
hr_breakable = 1
hr_funout = 0
hr_ignore_keyword_subst = 1
hr_ignore_white = 1
hr_intraline = 0
http_expiration_time = 600
limit_changes = 100
log_sort = date
mangle_email_addresses = 1
root_as_url_component = 1
short_log_len = 80
show_log_in_markup = 1
show_logs = 1
show_subdir_lastmod = 0
sort_by = file
sort_group_dirs = 1
svn_config_dir =
template_dir = templates
use_cvsgraph = 1
use_localtime = 1
use_pagesize = 0
use_rcsparse = 0
use_re_search = 0
[templates]
[cvsdb]
enabled = 1
host = $DB_HOST
socket = $DB_SOCKET
database_name = $DB
user = $DB_USER
passwd = $DB_PASSWD
readonly_user = $DB_USER
readonly_passwd = $DB_PASSWD
[vhosts]
[authz-forbidden]
forbidden =
[authz-forbiddenre]
forbiddenre = $SVN_FORBIDDENRE
[authz-svnauthz]
authzfile =
EOF
echo "*** Initializing database: $DB using $DB_USER@$DB_HOST"
$VIEWVC_DIR/bin/make-database <<EOF
$DB_HOST
$DB_USER
$DB_PASSWD
$DB
EOF
echo "*** Configuring Apache"
cat >/etc/apache2/sites-available/viewvc <<EOF
<VirtualHost *:80>
ServerName viewvc.office.custis.ru
ServerAlias viewvc
ServerAdmin sysadmins@custis.ru
ServerSignature Off
ErrorLog /var/log/apache2/viewvc-error.log
TransferLog /var/log/apache2/viewvc-access.log
LogLevel warn
# ViewVC at $VIEWVC_URI installed at $VIEWVC_DIR
DocumentRoot $VIEWVC_DIR/bin/mod_python
Alias $VIEWVC_STATIC_URI $VIEWVC_DIR/templates/docroot
Alias $VIEWVC_URI $VIEWVC_DIR/bin/mod_python/
<Location ~ ^${VIEWVC_URI_SLASH}*(\?.*)?$>
Options -Indexes
RewriteEngine On
RewriteRule .* ${VIEWVC_URI_SLASH}viewvc.py%{REQUEST_URI} [R,L]
</Location>
<Directory $VIEWVC_DIR/bin/mod_python>
Options +ExecCGI
AddHandler python-program .py
PythonHandler handler
PythonDebug Off
</Directory>
</VirtualHost>
EOF
a2enmod python
a2enmod mod_python
a2enmod rewrite
a2ensite viewvc
echo "*** Restarting Apache"
apache2ctl stop
sleep 1
apache2ctl start
echo "*** Building commit database for CVS"
for CVSROOT in $CVSROOTS; do
$VIEWVC_DIR/bin/cvsdbadmin rebuild $CVSROOT
done;
echo "*** Building commit database for Subversion repositories"
for i in `ls $SVNROOT`; do
if [ -d "$SVNROOT/$i" ]; then
$VIEWVC_DIR/bin/svndbadmin -v rebuild "$SVNROOT/$i"
fi
done;
# setup hooks for CVS
./setup-cvs-hooks "$CVSROOTS" "$VIEWVC_DIR" "$CVS_USER" "$CVS_GROUP" "$CVS_ASYNC"
# setup hooks for Subversion
./setup-svn-hooks "$SVNROOT" "$VIEWVC_DIR" "$SVN_ASYNC"

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/python
# -*-python-*-
#
# Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
@@ -44,17 +44,23 @@ import string
import cvsdb
import viewvc
import vclib.ccvs
from stat import *
def UpdateFile(db, repository, path, update, quiet_level):
def UpdateFile(db, repository, path, update, latest_checkin, quiet_level):
try:
if update:
mtime = os.stat(repository.rcsfile(path, 1))[ST_MTIME]
if mtime < latest_checkin:
return
commit_list = cvsdb.GetUnrecordedCommitList(repository, path, db)
else:
commit_list = cvsdb.GetCommitListFromRCSFile(repository, path)
except cvsdb.error, e:
print '[ERROR] %s' % (e)
return
except vclib.ItemNotFound, e:
return
file = string.join(path, "/")
printing = 0
@@ -77,7 +83,8 @@ def UpdateFile(db, repository, path, update, quiet_level):
print
def RecurseUpdate(db, repository, directory, update, quiet_level):
def RecurseUpdate(db, repository, directory, update, latest_checkin,
quiet_level):
for entry in repository.listdir(directory, None, {}):
path = directory + [entry.name]
@@ -85,11 +92,13 @@ def RecurseUpdate(db, repository, directory, update, quiet_level):
continue
if entry.kind is vclib.DIR:
RecurseUpdate(db, repository, path, update, quiet_level)
RecurseUpdate(db, repository, path, update, latest_checkin,
quiet_level)
continue
if entry.kind is vclib.FILE:
UpdateFile(db, repository, path, update, quiet_level)
UpdateFile(db, repository, path, update, latest_checkin,
quiet_level)
def RootPath(path, quiet_level):
"""Break os path into cvs root path and other parts"""
@@ -187,8 +196,11 @@ if __name__ == '__main__':
if command in ('rebuild', 'update'):
repository = vclib.ccvs.CVSRepository(None, rootpath, None,
cfg.utilities, 0)
latest_checkin = db.GetLatestCheckinTime(repository)
if latest_checkin is None:
command = 'rebuild'
RecurseUpdate(db, repository, path_parts,
command == 'update', quiet_level)
command == 'update', latest_checkin, quiet_level)
except KeyboardInterrupt:
print
print '** break **'

57
bin/cvsnt-import-cvsacl Executable file
View File

@@ -0,0 +1,57 @@
#!/usr/bin/perl
use strict;
my ($type, $path, $branch, $user, $perm);
my $R = ''; # -R for recursive
my $lvl = {
r => 1,
t => 2,
w => 3,
c => 4,
a => 4,
p => 5,
};
while(<>)
{
chomp;
next if /^\s*#/so;
($type, $path, $branch, $user, $perm) = split /:/, $_;
($user, $perm) = split /!/, $user;
next unless $perm;
$perm = [ sort { $lvl->{$b} cmp $lvl->{$a} } split //, $perm ];
$perm = $perm->[0];
if ($perm eq 't')
{
$perm = 'read,tag,nowrite,nocreate,nocontrol';
}
elsif ($perm eq 'r')
{
$perm = 'read,notag,nowrite,nocreate,nocontrol';
}
elsif ($perm eq 'w')
{
$perm = 'read,tag,write,nocreate,nocontrol';
}
elsif ($perm eq 'c' || $perm eq 'a')
{
$perm = 'read,tag,write,create,nocontrol';
}
elsif ($perm eq 'p')
{
$perm = 'read,tag,write,create,control';
}
print "cvs rchacl$R -a $perm";
print " -u '$user'" if $user ne 'ALL';
print " -r '$branch'" if $branch ne 'ALL';
if ($path ne 'ALL')
{
print " '$path'";
}
else
{
print ' `ls $CVSROOT | grep -v '."'#cvs'`";
}
print "\n";
}

View File

@@ -62,7 +62,8 @@ CREATE TABLE checkins (
KEY repositoryid_2 (repositoryid),
KEY dirid (dirid),
KEY fileid (fileid),
KEY branchid (branchid)
KEY branchid (branchid),
KEY descid (descid)
) TYPE=MyISAM;
DROP TABLE IF EXISTS descs;
@@ -71,7 +72,8 @@ CREATE TABLE descs (
description text,
hash bigint(20) DEFAULT '0' NOT NULL,
PRIMARY KEY (id),
KEY hash (hash)
KEY hash (hash),
FULLTEXT KEY description (description)
) TYPE=MyISAM;
DROP TABLE IF EXISTS dirs;

View File

@@ -1,3 +1,3 @@
AddHandler python-program .py
PythonHandler handler
PythonDebug On
PythonDebug Off

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2010 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
@@ -65,10 +65,7 @@ cfg = viewvc.load_config(CONF_PATHNAME)
def index(req):
server = sapi.ModPythonServer(req)
try:
viewvc_base_url = cfg.query.viewvc_base_url
if viewvc_base_url is None:
viewvc_base_url = "viewvc.py"
query.main(server, cfg, viewvc_base_url)
query.main(server, cfg, "viewvc.py")
finally:
server.close()

57
bin/setup-cvs-hooks Executable file
View File

@@ -0,0 +1,57 @@
#!/bin/sh
CVSROOTS=$1
VIEWVC_DIR=$2
CVS_USER=$3
CVS_GROUP=$4
CVS_ASYNC=$5
if [ ! "$CVSROOTS" -o ! "$VIEWVC_DIR" -o ! "$CVS_USER" -o ! "$CVS_GROUP" ]; then
echo "USAGE: $0 <CVS_ROOTS> <VIEWVC_DIR> <CVS_USER> <CVS_GROUP> [CVS_ASYNC]"
exit
fi
echo "*** Setting up commit hooks for CVS repositories (job=$CVS_ASYNC)"
chgrp $CVS_GROUP $VIEWVC_DIR
chmod 775 $VIEWVC_DIR
chmod 1777 .
for CVSROOT in $CVSROOTS; do
vcrootname=`basename $CVSROOT`
if [ "$CVS_ASYNC" ]; then
VHOOK="touch $VIEWVC_DIR/.cvs-updated-$vcrootname"
else
VHOOK="$VIEWVC_DIR/bin/cvsdbadmin update $CVSROOT >/dev/null"
fi;
su $CVS_USER -s /bin/sh <<EOSH
CVSROOT=$CVSROOT cvs co CVSROOT
if [ $? -eq 0 ]; then
cd CVSROOT
mv loginfo loginfo.bak
grep -v '/.cvs-updated' <loginfo.bak | grep -v '/bin/cvsdbadmin update' >loginfo
cat >>loginfo <<EOF
ALL $VHOOK
EOF
cvs ci -m 'CVS commit hook for ViewVC' loginfo
cd ..
rm -Rf CVSROOT
fi
EOSH
if [ "$CVS_ASYNC" ]; then
cat >$VIEWVC_DIR/cvshook-$vcrootname <<EOF
#!/bin/sh
if [ -e "$VIEWVC_DIR/.cvs-updated-$vcrootname" ]; then
rm "$VIEWVC_DIR/.cvs-updated-$vcrootname"
$VIEWVC_DIR/bin/cvsdbadmin update $CVSROOT >/dev/null
fi
EOF
chmod 755 $VIEWVC_DIR/cvshook-$vcrootname
cat >/etc/cron.d/viewvc-cvs-$vcrootname <<EOF
*/5 * * * * root $VIEWVC_DIR/cvshook-$vcrootname
EOF
fi
done;
if [ "$CVS_ASYNC" ]; then
echo "*** Reloading cron daemon"
/etc/init.d/cron reload
fi

36
bin/setup-svn-hooks Executable file
View File

@@ -0,0 +1,36 @@
#!/bin/sh
SVNROOT=$1
VIEWVC_DIR=$2
SVN_ASYNC=$3
if [ ! "$SVNROOT" -o ! "$VIEWVC_DIR" ]; then
echo "USAGE: $0 <SVNROOT> <VIEWVC_DIR>"
exit
fi
echo "*** Setting up commit hooks for Subversion"
for i in `ls $SVNROOT`; do
if [ -d "$SVNROOT/$i" ]; then
if [ "$SVN_ASYNC" ]; then
cat >"$SVNROOT/$i/hooks/post-commit.tmp" <<EOF
#!/bin/sh
echo "\$1 \$2" >> $VIEWVC_DIR/.svn-updated
EOF
else
cat >"$SVNROOT/$i/hooks/post-commit.tmp" <<EOF
#!/bin/sh
$VIEWVC_DIR/bin/svndbadmin update \$1 \$2
EOF
fi
chmod 755 "$SVNROOT/$i/hooks/post-commit.tmp"
mv "$SVNROOT/$i/hooks/post-commit.tmp" "$SVNROOT/$i/hooks/post-commit"
fi
done;
if [ "$SVN_ASYNC" ]; then
cat >/etc/cron.d/viewvc-svn <<EOF
*/10 * * * * root $VIEWVC_DIR/bin/svnupdate-async.sh "$VIEWVC_DIR"
EOF
/etc/init.d/cron reload
fi

File diff suppressed because it is too large Load Diff

View File

@@ -158,6 +158,7 @@ class SvnRev:
svn.repos.svn_repos_replay(fsroot, e_ptr, e_baton)
self.changes = []
changes_hash = {}
for path, change in editor.changes.items():
# skip non-file changes
if change.item_kind != svn.core.svn_node_file:
@@ -181,6 +182,14 @@ class SvnRev:
change.path and change.path or None)
diff_fp = diffobj.get_pipe()
plus, minus = _get_diff_counts(diff_fp)
# CustIS Bug 50473: a workaround for svnlib behaviour in file movements (FILE1 -> FILE2 + FILE1 -> null)
if change.base_path:
if not change.path and changes_hash.get(change.base_path, '') != '':
minus = 0
elif change.path:
changes_hash[change.base_path] = change.path
self.changes.append((path, action, plus, minus))
def _get_root_for_rev(self, rev):
@@ -242,7 +251,6 @@ def main(command, repository, revs=[], verbose=0, force=0):
cfg = viewvc.load_config(CONF_PATHNAME)
db = cvsdb.ConnectDatabase(cfg)
# Purge what must be purged.
if command in ('rebuild', 'purge'):
if verbose:
print "Purging commit info for repository root `%s'" % repository
@@ -253,24 +261,18 @@ def main(command, repository, revs=[], verbose=0, force=0):
sys.stderr.write("ERROR: " + str(e) + "\n")
sys.exit(1)
# Record what must be recorded.
if command in ('rebuild', 'update'):
if not os.path.exists(repository):
sys.stderr.write('ERROR: could not find repository %s\n'
% (repository))
sys.exit(1)
repo = SvnRepo(repository)
if command == 'rebuild' or (command == 'update' and not revs):
for rev in range(repo.rev_max+1):
handle_revision(db, command, repo, rev, verbose)
elif command == 'update':
if revs[0] is None:
revs[0] = repo.rev_max
if revs[1] is None:
revs[1] = repo.rev_max
revs.sort()
for rev in range(revs[0], revs[1]+1):
handle_revision(db, command, repo, rev, verbose, force)
repo = SvnRepo(repository)
if command == 'rebuild' or (command == 'update' and not revs):
for rev in range(repo.rev_max+1):
handle_revision(db, command, repo, rev, verbose)
elif command == 'update':
if revs[0] is None:
revs[0] = repo.rev_max
if revs[1] is None:
revs[1] = repo.rev_max
revs.sort()
for rev in range(revs[0], revs[1]+1):
handle_revision(db, command, repo, rev, verbose, force)
def _rev2int(r):
if r == 'HEAD':
@@ -337,6 +339,12 @@ if __name__ == '__main__':
sys.stderr.write('ERROR: unknown command %s\n' % command)
usage()
repository = args[2]
if not os.path.exists(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:
if command == 'rebuild':
@@ -359,7 +367,6 @@ if __name__ == '__main__':
rev = None
try:
repository = vclib.svn.canonicalize_rootpath(args[2])
repository = cvsdb.CleanRepository(os.path.abspath(repository))
main(command, repository, revs, verbose, force)
except KeyboardInterrupt:

50
bin/svnupdate-async Executable file
View File

@@ -0,0 +1,50 @@
#!/usr/bin/perl
# Скрипт для обновления SVN репозиториев svndbadmin-ом
# Берёт номера ревизий и имена репозиториев из перечисленных файлов или STDIN,
# группирует их по номерам и выводит список команд, необходимых для обновления
use strict;
# первый аргумент - путь к svndbadmin
my $svndbadmin = shift @ARGV
|| die "USAGE: $0 <path_to_svndbadmin> FILES...";
# считываем названия репозиториев и номера ревизий из файла
my $tou = {};
my ($repos, $rev);
while (<>)
{
s/^\s+//so;
s/\s+$//so;
($repos, $rev) = split /\s+/, $_;
$tou->{$repos}->{$rev} = 1;
}
# превращаем номера ревизий в диапазоны ревизий
my ($i, $j, $r, $nr);
foreach $repos (keys %$tou)
{
$rev = [ sort keys %{$tou->{$repos}} ];
$nr = [];
$j = 0;
for $i (1..@$rev)
{
if ($i > $#$rev || $rev->[$i]-$rev->[$j] > $i-$j)
{
$r = $rev->[$j];
$r .= ":".$rev->[$i-1] if $i-$j > 1;
push @$nr, $r;
$j = $i;
}
}
$tou->{$repos} = $nr;
}
# выводим список команд для выполнения
foreach $repos (keys %$tou)
{
foreach (@{$tou->{$repos}})
{
print "$svndbadmin update $repos $_\n";
}
}

8
bin/svnupdate-async.sh Executable file
View File

@@ -0,0 +1,8 @@
#!/bin/sh
VIEWVC_DIR=$1
test -f "$VIEWVC_DIR/.svn-updating" -o ! -f "$VIEWVC_DIR/.svn-updated" && exit 0
mv "$VIEWVC_DIR/.svn-updated" "$VIEWVC_DIR/.svn-updating"
"$VIEWVC_DIR/bin/svnupdate-async" "$VIEWVC_DIR/bin/svndbadmin" "$VIEWVC_DIR/.svn-updating" | sh >/dev/null
rm "$VIEWVC_DIR/.svn-updating"

View File

@@ -1,54 +0,0 @@
#!/usr/bin/python
# -*-python-*-
#
# Copyright (C) 1999-2010 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
# distribution or at http://viewvc.org/license-1.html.
#
# For more information, visit http://viewvc.org/
#
# -----------------------------------------------------------------------
#
# viewvc: View CVS/SVN repositories via a web browser
#
# -----------------------------------------------------------------------
#
# This is a fcgi entry point for the query ViewVC app. It's appropriate
# for use with mod_fcgid and flup. It defines a single application function
# that is a valid fcgi entry point.
#
# mod_fcgid: http://httpd.apache.org/mod_fcgid/
# flup:
# http://pypi.python.org/pypi/flup
# http://trac.saddi.com/flup
#
# -----------------------------------------------------------------------
import sys, os
LIBRARY_DIR = None
CONF_PATHNAME = None
if LIBRARY_DIR:
sys.path.insert(0, LIBRARY_DIR)
else:
sys.path.insert(0, os.path.abspath(os.path.join(sys.argv[0],
"../../../lib")))
import sapi
import viewvc
import query
from flup.server import fcgi
def application(environ, start_response):
server = sapi.WsgiServer(environ, start_response)
cfg = viewvc.load_config(CONF_PATHNAME, server)
viewvc_base_url = cfg.query.viewvc_base_url
if viewvc_base_url is None:
viewvc_base_url = "viewvc.fcgi"
query.main(server, cfg, viewvc_base_url)
return []
fcgi.WSGIServer(application).run()

View File

@@ -1,45 +0,0 @@
# -*-python-*-
#
# Copyright (C) 1999-2010 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
# distribution or at http://viewvc.org/license-1.html.
#
# For more information, visit http://viewvc.org/
#
# -----------------------------------------------------------------------
#
# viewvc: View CVS/SVN repositories via a web browser
#
# -----------------------------------------------------------------------
#
# This is a wsgi entry point for the query ViewVC app. It's appropriate
# for use with mod_wsgi. It defines a single application function that
# is a valid wsgi entry point.
#
# -----------------------------------------------------------------------
import sys, os
LIBRARY_DIR = None
CONF_PATHNAME = None
if LIBRARY_DIR:
sys.path.insert(0, LIBRARY_DIR)
else:
sys.path.insert(0, os.path.abspath(os.path.join(sys.argv[0],
"../../../lib")))
import sapi
import viewvc
import query
def application(environ, start_response):
server = sapi.WsgiServer(environ, start_response)
cfg = viewvc.load_config(CONF_PATHNAME, server)
viewvc_base_url = cfg.query.viewvc_base_url
if viewvc_base_url is None:
viewvc_base_url = "viewvc.wsgi"
query.main(server, cfg, viewvc_base_url)
return []

View File

@@ -1,50 +0,0 @@
#!/usr/bin/python
# -*-python-*-
#
# Copyright (C) 1999-2010 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
# distribution or at http://viewvc.org/license-1.html.
#
# For more information, visit http://viewvc.org/
#
# -----------------------------------------------------------------------
#
# viewvc: View CVS/SVN repositories via a web browser
#
# -----------------------------------------------------------------------
#
# This is a fcgi entry point for the main ViewVC app. It's appropriate
# for use with mod_fcgid and flup. It defines a single application function
# that is a valid fcgi entry point.
#
# mod_fcgid: http://httpd.apache.org/mod_fcgid/
# flup:
# http://pypi.python.org/pypi/flup
# http://trac.saddi.com/flup
#
# -----------------------------------------------------------------------
import sys, os
LIBRARY_DIR = None
CONF_PATHNAME = None
if LIBRARY_DIR:
sys.path.insert(0, LIBRARY_DIR)
else:
sys.path.insert(0, os.path.abspath(os.path.join(sys.argv[0],
"../../../lib")))
import sapi
import viewvc
from flup.server import fcgi
def application(environ, start_response):
server = sapi.WsgiServer(environ, start_response)
cfg = viewvc.load_config(CONF_PATHNAME, server)
viewvc.main(server, cfg)
return []
fcgi.WSGIServer(application).run()

View File

@@ -1,41 +0,0 @@
# -*-python-*-
#
# Copyright (C) 1999-2009 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
# distribution or at http://viewvc.org/license-1.html.
#
# For more information, visit http://viewvc.org/
#
# -----------------------------------------------------------------------
#
# viewvc: View CVS/SVN repositories via a web browser
#
# -----------------------------------------------------------------------
#
# This is a wsgi entry point for the main ViewVC app. It's appropriate
# for use with mod_wsgi. It defines a single application function that
# is a valid wsgi entry point.
#
# -----------------------------------------------------------------------
import sys, os
LIBRARY_DIR = None
CONF_PATHNAME = None
if LIBRARY_DIR:
sys.path.insert(0, LIBRARY_DIR)
else:
sys.path.insert(0, os.path.abspath(os.path.join(sys.argv[0],
"../../../lib")))
import sapi
import viewvc
def application(environ, start_response):
server = sapi.WsgiServer(environ, start_response)
cfg = viewvc.load_config(CONF_PATHNAME, server)
viewvc.main(server, cfg)
return []

File diff suppressed because it is too large Load Diff

View File

@@ -161,6 +161,12 @@ td {
resource. Valid only when <var>pathtype</var> is <tt>file</tt>
or (for Subversion roots) <tt>dir</tt>.</td>
</tr>
<tr class="varlevel1">
<td class="varname">log_rev_href</td>
<td>String</td>
<td>Revision number of the file-revision currently being viewed, or
None.</td>
</tr>
<tr class="varlevel1">
<td class="varname">nav_path</td>
<td>List</td>
@@ -315,7 +321,7 @@ td {
<tr class="varlevel1">
<td class="varname">pathrev_hidden_values</td>
<td>List</td>
<td>Hidden field name/value pairs for the revision/tag selection form.</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>
@@ -325,7 +331,7 @@ td {
<tr class="varlevel1">
<td class="varname">pathrev_clear_hidden_values</td>
<td>List</td>
<td>Hidden field name/value pairs for the path revision clear button.</td>
<td>Hidden value name/value pairs for the path revision clear button.</td>
</tr>
</tbody>
</table>
@@ -444,10 +450,10 @@ td {
<tr class="varlevel1">
<td class="varname">annotation</td>
<td>String</td>
<td>Valid values are "none" (no annotations were attempted),
"annotated" (annotation was successful), "binary" (file contents
are not line-based and human-readable), and "error" (something
went wrong during annotation).</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>
@@ -719,29 +725,19 @@ td {
<tr class="varlevel1">
<td class="varname">diff_format_hidden_values</td>
<td>List</td>
<td>Hidden field name/value pairs for the diff format selection form.</td>
<td>Hidden value name/value pairs for the diff format selection form.</td>
</tr>
<tr class="varlevel1">
<td class="varname">left</td>
<td>Container</td>
<td>Container object for grouping information about the left file.</td>
</tr>
<tr class="varlevel2">
<td class="varname">left.ago</td>
<td>String</td>
<td>Text description of the time elapsed since <var>left.date</date>.</td>
</tr>
<tr class="varlevel2">
<td class="varname">left.annotate_href</td>
<td>String</td>
<td>URL of the ViewVC annotation view for the left file.
Valid only when <var>entries.pathtype</var> is <tt>file</tt>.</td>
</tr>
<tr class="varlevel2">
<td class="varname">left.author</td>
<td>String</td>
<td>Author of the revision of the left file.</td>
</tr>
<tr class="varlevel2">
<td class="varname">left.date</td>
<td>String</td>
@@ -759,11 +755,6 @@ td {
<td>URL to download the HEAD revision of the left file as
<tt>text/plain</tt>.</td>
</tr>
<tr class="varlevel2">
<td class="varname">left.log</td>
<td>String</td>
<td>Log message of the left file revision.</td>
</tr>
<tr class="varlevel2">
<td class="varname">left.path</td>
<td>String</td>
@@ -787,11 +778,6 @@ td {
current revision. Valid only when <var>roottype</var> is
<tt>svn</tt>.</td>
</tr>
<tr class="varlevel2">
<td class="varname">left.size</td>
<td>String</td>
<td>Size of the left file revision, in bytes. Subversion only.</td>
</tr>
<tr class="varlevel2">
<td class="varname">left.tag</td>
<td>String</td>
@@ -813,22 +799,12 @@ td {
<td>Container</td>
<td>Container object for grouping information about the right file.</td>
</tr>
<tr class="varlevel2">
<td class="varname">right.author</td>
<td>String</td>
<td>Author of the revision of the right file.</td>
</tr>
<tr class="varlevel2">
<td class="varname">right.annotate_href</td>
<td>String</td>
<td>URL of the ViewVC annotation view for the right file.
Valid only when <var>entries.pathtype</var> is <tt>file</tt>.</td>
</tr>
<tr class="varlevel2">
<td class="varname">right.author</td>
<td>String</td>
<td>Author of the revision of the right file.</td>
</tr>
<tr class="varlevel2">
<td class="varname">right.date</td>
<td>String</td>
@@ -846,11 +822,6 @@ td {
<td>URL to download the HEAD revision of the right file as
<tt>text/plain</tt>.</td>
</tr>
<tr class="varlevel2">
<td class="varname">right.log</td>
<td>String</td>
<td>Log message of the right file revision.</td>
</tr>
<tr class="varlevel2">
<td class="varname">right.path</td>
<td>String</td>
@@ -874,11 +845,6 @@ td {
current revision. Valid only when <var>roottype</var> is
<tt>svn</tt>.</td>
</tr>
<tr class="varlevel2">
<td class="varname">right.size</td>
<td>String</td>
<td>Size of the right file revision, in bytes. Subversion only.</td>
</tr>
<tr class="varlevel2">
<td class="varname">right.tag</td>
<td>String</td>
@@ -939,7 +905,7 @@ td {
<tr class="varlevel1">
<td class="varname">dir_paging_hidden_values</td>
<td>List</td>
<td>Hidden field name/value pairs for the page selection form.</td>
<td>Hidden value name/value pairs for the page selection form.</td>
</tr>
<tr class="varlevel1">
<td class="varname">entries</td>
@@ -1121,16 +1087,23 @@ td {
<td>String</td>
<td>Current search expression, if any.</td>
</tr>
<tr class="varlevel1">
<td class="varname">search_re_form</td>
<td>Boolean</td>
<td>Indicates whether or not to display the regular expression search
form. Value depends on the whether searching is enabled in the
configuration and whether or not the current directory is
empty.</td>
</tr>
<tr class="varlevel1">
<td class="varname">search_re_action</td>
<td>String</td>
<td>Form action URL for the regular expression search form,
if searching is available.</td>
<td>Form action URL for the regular expression search form.</td>
</tr>
<tr class="varlevel1">
<td class="varname">search_re_hidden_values</td>
<td>List</td>
<td>Hidden field name/value pairs for the regular expression search form.</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>
@@ -1276,7 +1249,7 @@ td {
<tr class="varlevel1">
<td class="varname">diff_select_hidden_values</td>
<td>List</td>
<td>Hidden field name/value pairs for the diff selection form.</td>
<td>Hidden value name/value pairs for the diff selection form.</td>
</tr>
<tr class="varlevel1">
<td class="varname">entries</td>
@@ -1557,7 +1530,7 @@ td {
<tr class="varlevel1">
<td class="varname">log_paging_hidden_values</td>
<td>List</td>
<td>Hidden field name/value pairs for the page selection form.</td>
<td>Hidden value name/value pairs for the page selection form.</td>
</tr>
<tr class="varlevel1">
<td class="varname">logsort</td>
@@ -1573,7 +1546,7 @@ td {
<tr class="varlevel1">
<td class="varname">logsort_hidden_values</td>
<td>List</td>
<td>Hidden field name/value pairs for the log sort drop down box</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>
@@ -1937,7 +1910,7 @@ td {
<tr class="varlevel1">
<td class="varname">query_hidden_values</td>
<td>List</td>
<td>Hidden field name/value pairs for query form.</td>
<td>Hidden value name/value pairs for query form.</td>
</tr>
<tr class="varlevel1">
<td class="varname">querysort</td>
@@ -2077,7 +2050,7 @@ td {
<tr class="varlevel1">
<td class="varname">jump_rev_hidden_values</td>
<td>List</td>
<td>Hidden field name/value pairs for revision jump form.</td>
<td>Hidden value name/value pairs for revision jump form.</td>
</tr>
<tr class="varlevel1">
<td class="varname">limit_changes</td>
@@ -2100,11 +2073,6 @@ td {
<td>String</td>
<td>URL for the current view but with <tt>limit_changes</tt> disabled.</td>
</tr>
<tr class="varlevel1">
<td class="varname">num_changes</td>
<td>String</td>
<td>Number of paths changed in this revision.</td>
</tr>
<tr class="varlevel1">
<td class="varname">next_href</td>
<td>String</td>

View File

@@ -137,8 +137,9 @@ td {
configuration's "general" section.</li>
<li>Finally, ensure that that the new <code>authorizer</code>
option is set to either "forbidden" or "forbiddenre", depending
on which of those you were using in ViewVC 1.0.x.</li>
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>
@@ -214,9 +215,10 @@ td {
<li>options/py2html_path</li>
<li>options/use_enscript</li>
<li>options/use_highlight</li>
<li>options/use_pagesize</li>
<li>options/use_php</li>
<li>options/use_py2html</li>
<li>options/use_pygments</li>
<li>options/use_source_highlight</li>
</ul>
</div>
@@ -224,23 +226,16 @@ td {
<div class="h3">
<h3>Checkin Database</h3>
<p>ViewVC 1.1 introduces to the <code>cvsdbadmin</code>
and <code>svndbadmin</code> tools a new "purge" operation, which
allows you to remove all the information related to a given root
from your checkins database (without disturbing the information
associated with other roots). Likewise, the "rebuild" command in
those tools now implies a "purge" followed by an update.</p>
<p>As a related change, the <code>svndbadmin</code> program's
"rebuild" subcommand has had its purpose become more defined. It
no longer accepts a revision argument, and therefore can now only
be used to completely rebuild the entirety of the checkin database
information for a Subversion repository (instead of being able to
only update the information related to single Subversion revision).
For per-revision updating, use <code>svndbadmin update</code> and
<p>In ViewVC 1.1, the <code>svndbadmin</code> program's "rebuild"
subcommand has had its purpose become more defined. It no longer
accepts a revision argument, and therefore can now only be used to
completely rebuild the entirety of the checkin database information
for a Subversion repository (instead of being able to only update
the information related to single Subversion revision). For
per-revision updating, use <code>svndbadmin update</code> and
provide a revision (or revision range). And to get the previous
rebuild-a-revision effect, pass the new <code>--force</code> option
to <code>svndbadmin update</code>.</p>
rebuild-a-revision effect, pass the new <code>--force</code>
option to <code>svndbadmin update</code>.</p>
<p>In other words, where you once did this:</p>
@@ -254,19 +249,6 @@ td {
</pre>
</blockquote>
<p>To enhance the performance of the new "purge" operation, ViewVC 1.1
introduces some slight changes to the checkin database schema. If
you use the <code>make-database</code> tool to (re)create your
checkins database, it will by default employ the new database
schema. This should cause the database to be virtually unusable by
previous versions of ViewVC, and that's by design. If, however,
you need to (re)create your checkins database and you require
compatibility with previous versions of ViewVC or ViewCVS, simply
pass the "--version=1.0" option to the <code>make-database</code>
script. Note that your "purge" and "rebuild" operations could be
abysmally slow, though, as that version of the database schema is
not optimized for those operations.</p>
</div>
<div class="h3">
@@ -314,22 +296,6 @@ td {
<li>options/allow_tar</li>
</ul>
<p>ViewVC now honors the "svn:mime-type" property stored on
Subversion-versioned files as the primary source of MIME type
determination (before falling back to name-based MIME mappings and
such). However, this can negatively affect the viewability of
certain files &mdash; especially images &mdash; whose
"svn:mime-type" properties are set incorrectly, such as will happen
if Subversion itself merely determines that the file isn't
human-readable and uses the "application/octet-stream" MIME type to
record this determination. To make ViewVC <em>not</em> honor the
"svn:mime-type" property value, set the <code>svn_ignore_mimetype</code>
configuration option.</p>
<p>Speaking of MIME types, the option <code>mime_types_file</code> is
now <code>mime_types_files</code>, as it now carries multiple paths
to MIME mappings files, ordered by preference.</p>
<p>The <code>use_rcsparse</code> option was moved from the "general"
section to the "options" section.</p>
@@ -348,7 +314,7 @@ td {
all = viewvc.*
[all-options]
allowed_views = annotate, diff, markup, tar
allow_tar = 1
</pre>
</blockquote>
@@ -358,7 +324,7 @@ allowed_views = annotate, diff, markup, tar
all = viewvc.*
[vhost-all/options]
allowed_views = annotate, diff, markup, tar
allow_tar = 1
</pre>
</blockquote>
@@ -665,11 +631,6 @@ allowed_views = annotate, diff, markup, tar
<td>revision.ezt</td>
<td>now is an iterable list of objects with .name and .value attributes</td>
</tr>
<tr class="added">
<td class="varname">num_changes</td>
<td>revision.ezt</td>
<td>added</td>
</tr>
</tbody>
</table>
@@ -698,7 +659,7 @@ allowed_views = annotate, diff, markup, tar
<h3>Checkin Database</h3>
<p>ViewVC 1.0 reads and writes commit times in the MySQL database in
UTC time rather than local time. This can cause times displayed on
UTC time rather than local time. This can cause times displayed on
the query page to be a few hours off if an old database is being
used with a new version of ViewVC. The best way to fix this is to
rebuild the database with the new version of cvsdbadmin, but it
@@ -741,8 +702,7 @@ allowed_views = annotate, diff, markup, tar
<li>options/root_as_url_component</li>
<li>options/default_file_view</li>
<li>options/sort_group_dirs</li>
<li>options/dir_pagesize</li>
<li>options/log_pagesize</li>
<li>options/use_pagesize</li>
<li>options/limit_changes</li>
<li>options/use_localtime</li>
<li>options/cross_copies</li>

View File

@@ -996,7 +996,7 @@ th.caption {
<td>file query string</td>
</tr>
<tr>
<td><code>file_match=<var>FILE_MATCH</var></code></td>
<td><code>file_match=FILE_MATCH</code></td>
<td>optional</td>
<td>"exact" "like" "glob" "regex" or "notregex" determining type
of file match</td>
@@ -1007,7 +1007,7 @@ th.caption {
<td>author query string</td>
</tr>
<tr>
<td><code>who_match=<var>WHO_MATCH</var></code></td>
<td><code>who_match=WHO_MATCH</code></td>
<td>optional</td>
<td>"exact" "like" "glob" "regex" or "notregex" determining type
of author match</td>
@@ -1024,36 +1024,36 @@ th.caption {
of log message match</td>
</tr>
<tr>
<td><code>querysort=<var>SORT</var></code></td>
<td><code>querysort=SORT</code></td>
<td>optional</td>
<td>"date" "author" or "file" determining order of query results</td>
</tr>
<tr>
<td><code>date=<var>DATE</var></code></td>
<td><code>date=DATE</code></td>
<td>optional</td>
<td>"hours" "day" "week" "month" "all" or "explicit" to filter
query results by date</td>
</tr>
<tr>
<td><code>hours=<var>HOURS</var></code></td>
<td><code>hours=HOURS</code></td>
<td>optional</td>
<td>number of hours back to include results from when
<code><var>DATE</var></code> is "hours"</td>
</tr>
<tr>
<td><code>mindate=<var>MINDATE</var></code></td>
<td><code>mindate=MINDATE</code></td>
<td>optional</td>
<td>earliest date to include results from when
<code><var>DATE</var></code> is "explicit"</td>
</tr>
<tr>
<td><code>maxdate=<var>MAXDATE</var></code></td>
<td><code>maxdate=MAXDATE</code></td>
<td>optional</td>
<td>latest date to include results from when
<code><var>DATE</var></code> is "explicit"</td>
</tr>
<tr>
<td><code>limit_changes=<var>LIMIT_CHANGES</var></code></td>
<td><code>limit_changes=LIMIT_CHANGES</code></td>
<td>optional</td>
<td>maximum number of files to list per commit in query
results. Default is value of <code>limit_changes</code>
@@ -1113,7 +1113,7 @@ th.caption {
<td>branch query string</td>
</tr>
<tr>
<td><code>branch_match=<var>BRANCH_MATCH</var></code></td>
<td><code>branch_match=BRANCH_MATCH</code></td>
<td>optional</td>
<td>"exact" "like" "glob" "regex" or "notregex" determining type
of branch match</td>
@@ -1129,7 +1129,7 @@ th.caption {
<td>file query string</td>
</tr>
<tr>
<td><code>file_match=<var>FILE_MATCH</var></code></td>
<td><code>file_match=FILE_MATCH</code></td>
<td>optional</td>
<td>"exact" "like" "glob" "regex" or "notregex" determining type
of file match</td>
@@ -1140,7 +1140,7 @@ th.caption {
<td>author query string</td>
</tr>
<tr>
<td><code>who_match=<var>WHO_MATCH</var></code></td>
<td><code>who_match=WHO_MATCH</code></td>
<td>optional</td>
<td>"exact" "like" "glob" "regex" or "notregex" determining type
of author match</td>
@@ -1157,50 +1157,50 @@ th.caption {
of log message match</td>
</tr>
<tr>
<td><code>querysort=<var>SORT</var></code></td>
<td><code>querysort=SORT</code></td>
<td>optional</td>
<td>"date" "author" or "file" determining order of query results</td>
</tr>
<tr>
<td><code>date=<var>DATE</var></code></td>
<td><code>date=DATE</code></td>
<td>optional</td>
<td>"hours" "day" "week" "month" "all" or "explicit" to filter
query results by date</td>
</tr>
<tr>
<td><code>hours=<var>HOURS</var></code></td>
<td><code>hours=HOURS</code></td>
<td>optional</td>
<td>number of hours back to include results from when
<code><var>DATE</var></code> is "hours"</td>
</tr>
<tr>
<td><code>mindate=<var>MINDATE</var></code></td>
<td><code>mindate=MINDATE</code></td>
<td>optional</td>
<td>earliest date to include results from when
<code><var>DATE</var></code> is "explicit"</td>
</tr>
<tr>
<td><code>maxdate=<var>MAXDATE</var></code></td>
<td><code>maxdate=MAXDATE</code></td>
<td>optional</td>
<td>latest date to include results from when
<code><var>DATE</var></code> is "explicit"</td>
</tr>
<tr>
<td><code>format=<var>FORMAT</var></code></td>
<td><code>format=FORMAT</code></td>
<td>optional</td>
<td>"rss" or "backout" values to generate an rss feed or list of
commands to back out changes instead showing a normal query result
page</td>
</tr>
<tr>
<td><code>limit=<var>LIMIT</var></code></td>
<td><code>limit=LIMIT</code></td>
<td>optional</td>
<td>maximum number of file-revisions to process during a
query. Default is value of <code>row_limit</code> configuration
option</td>
</tr>
<tr>
<td><code>limit_changes=<var>LIMIT_CHANGES</var></code></td>
<td><code>limit_changes=LIMIT_CHANGES</code></td>
<td>optional</td>
<td>maximum number of files to list per commit in query
results. Default is value of <code>limit_changes</code>
@@ -1254,7 +1254,7 @@ th.caption {
<td><a href="#revision-param"><code>revision</code> parameter</a></td>
</tr>
<tr>
<td><code>limit_changes=<var>LIMIT_CHANGES</var></code></td>
<td><code>limit_changes=LIMIT_CHANGES</code></td>
<td>optional</td>
<td>maximum number of files to list per commit. Default is value
of <code>limit_changes</code> configuration option</td>

View File

@@ -1,54 +0,0 @@
TARGETS = python/elx-python java/elx-java
all : $(TARGETS)
CFLAGS = -g -Wpointer-arith -Wwrite-strings -Wshadow -Wall
CPPFLAGS = -Ipython -Ijava -I.
# the scanner depends on tokens generated from python.y
python/scanner.c : python/python.c
# the keywords also need the tokens in python.h
python/py_keywords.c : python/python.c
# we need the scanner tokens in py_scan.h and keywords in py_keywords.h
python/elx-python.o : python/elx-python.c python/py_keywords.c
# we need java.[ch] generated first to get the tokens in java.h
java/j_scan.c : java/java.c java/j_keywords.c
# the keywords also need the tokens in java.h
java/j_keywords.c : java/java.c
# we need the scanner tokens in j_scan.h and keywords in j_keywords.h
java/elx-java.o : java/elx-java.c java/j_keywords.c java/java.c
python/elx-python : python/elx-python.o python/scanner.o python/python.o \
python/py_keywords.o elx-common.o
$(CC) -o $@ $^
java/elx-java : java/elx-java.o java/j_scan.o java/java.o java/j_keywords.o \
elx-common.o
$(CC) -o $@ $^
clean :
rm -f *.o
rm -f python/*.o python/python.[ch] python/py_keywords.[ch]
rm -f java/*.o java/java.[ch] java/j_keywords.[ch] java/j_scan.[ch]
rm -f python/*.output java/*.output
rm -f $(TARGETS)
.SUFFIXES:
.SUFFIXES: .c .y .shilka .o
%.c : %.y
@d="`echo $@ | sed 's/\.c//'`" ; \
echo msta -d -enum -v -o $$d $< ; \
msta -d -enum -v -o $$d $<
%.c : %.shilka
@d="`echo $@ | sed 's,/[^/]*$$,,'`" ; \
f="`echo $< | sed 's,.*/,,'`" ; \
echo shilka -length -no-definitions -interface $$f ; \
(cd $$d && shilka -length -no-definitions -interface $$f)

View File

@@ -1,110 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "elx.h"
#define ELX_ELEMS_EXT ".elx"
#define ELX_SYMBOLS_EXT ".els"
static void usage(const char *progname)
{
fprintf(stderr, "USAGE: %s FILENAME\n", progname);
exit(1);
}
static const char * build_one(const char *base, int len, const char *suffix)
{
int slen = strlen(suffix);
char *fn;
fn = malloc(len + slen + 1);
memcpy(fn, base, len);
memcpy(fn + len, suffix, slen);
fn[len + slen] = '\0';
return fn;
}
elx_context_t *elx_process_args(int argc, const char **argv)
{
elx_context_t *ec;
const char *input_fn;
const char *p;
int len;
/* ### in the future, we can expand this for more options */
if (argc != 2)
{
usage(argv[0]);
/* NOTREACHED */
}
input_fn = argv[1];
p = strrchr(input_fn, '.');
if (p == NULL)
len = strlen(input_fn);
else
len = p - argv[1];
ec = malloc(sizeof(*ec));
ec->input_fn = input_fn;
ec->elx_fn = build_one(input_fn, len, ELX_ELEMS_EXT);
ec->sym_fn = build_one(input_fn, len, ELX_SYMBOLS_EXT);
return ec;
}
void elx_open_files(elx_context_t *ec)
{
const char *fn;
const char *op;
if ((ec->input_fp = fopen(ec->input_fn, "r")) == NULL)
{
fn = ec->input_fn;
op = "reading";
goto error;
}
if ((ec->elx_fp = fopen(ec->elx_fn, "w")) == NULL)
{
fn = ec->elx_fn;
op = "writing";
goto error;
}
if ((ec->sym_fp = fopen(ec->sym_fn, "w")) == NULL)
{
fn = ec->sym_fn;
op = "writing";
goto error;
}
return;
error:
fprintf(stderr, "ERROR: file \"%s\" could not be opened for %s.\n %s\n",
fn, op, strerror(errno));
exit(2);
}
void elx_close_files(elx_context_t *ec)
{
fclose(ec->input_fp);
fclose(ec->elx_fp);
fclose(ec->sym_fp);
}
void elx_issue_token(elx_context_t *ec,
char which, int start, int len,
const char *symbol)
{
fprintf(ec->elx_fp, "%c %d %d\n", which, start, len);
if (ELX_DEFINES_SYM(which))
{
fprintf(ec->sym_fp, "%s %d %s\n", symbol, start, ec->input_fn);
}
}

View File

@@ -1,54 +0,0 @@
#ifndef ELX_H
#define ELX_H
#include <stdio.h>
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#define ELX_COMMENT 'C' /* a comment */
#define ELX_STRING 'S' /* a string constant */
#define ELX_KEYWORD 'K' /* a language keyword */
#define ELX_GLOBAL_FDEF 'F' /* function defn in global (visible) scope */
#define ELX_LOCAL_FDEF 'L' /* function defn in local (hidden) scope */
#define ELX_METHOD_DEF 'M' /* method definition */
#define ELX_FUNC_REF 'R' /* function reference / call */
#define ELX_DEFINES_SYM(c) ((c) == ELX_GLOBAL_FDEF || (c) == ELX_LOCAL_FDEF \
|| (c) == ELX_METHOD_DEF)
typedef struct
{
/* input filename */
const char *input_fn;
/* output filenames: element extractions, and symbols */
const char *elx_fn;
const char *sym_fn;
/* file pointers for each of the input/output files */
FILE *input_fp;
FILE *elx_fp;
FILE *sym_fp;
} elx_context_t;
elx_context_t *elx_process_args(int argc, const char **argv);
void elx_open_files(elx_context_t *ec);
void elx_close_files(elx_context_t *ec);
void elx_issue_token(elx_context_t *ec,
char which, int start, int len,
const char *symbol);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* ELX_H */

View File

@@ -1,121 +0,0 @@
#!/usr/bin/env python
#
# generate HTML given an input file and an element file
#
import re
import string
import cgi
import struct
_re_elem = re.compile('([a-zA-Z]) ([0-9]+) ([0-9]+)\n')
CHUNK_SIZE = 98304 # 4096*24
class ElemParser:
"Parse an elements file, extracting the token type, start, and length."
def __init__(self, efile):
self.efile = efile
def get(self):
line = self.efile.readline()
if not line:
return None, None, None
t, s, e = string.split(line)
return t, int(s)-1, int(e)
def unused_get(self):
record = self.efile.read(9)
if not record:
return None, None, None
return struct.unpack('>cii', record)
class Writer:
"Generate output, including copying from another input."
def __init__(self, ifile, ofile):
self.ifile = ifile
self.ofile = ofile
self.buf = ifile.read(CHUNK_SIZE)
self.offset = 0
def write(self, data):
self.ofile.write(data)
def copy(self, pos, amt):
"Copy 'amt' bytes from position 'pos' of input to output."
idx = pos - self.offset
self.ofile.write(cgi.escape(buffer(self.buf, idx, amt)))
amt = amt - (len(self.buf) - idx)
while amt > 0:
self._more()
self.ofile.write(cgi.escape(buffer(self.buf, 0, amt)))
amt = amt - len(self.buf)
def flush(self, pos):
"Flush the rest of the input to the output."
idx = pos - self.offset
self.ofile.write(cgi.escape(buffer(self.buf, idx)))
while 1:
buf = self.ifile.read(CHUNK_SIZE)
if not buf:
break
self.ofile.write(cgi.escape(buf))
def _more(self):
self.offset = self.offset + len(self.buf)
self.buf = self.ifile.read(CHUNK_SIZE)
def generate(input, elems, output, genpage=0):
ep = ElemParser(elems)
w = Writer(input, output)
cur = 0
if genpage:
w.write('''\
<html><head><title>ELX Output Page</title>
<style type="text/css">
.elx_C { color: firebrick; font-style: italic; }
.elx_S { color: #bc8f8f; font-weight: bold; }
.elx_K { color: purple; font-weight: bold }
.elx_F { color: blue; font-weight: bold; }
.elx_L { color: blue; font-weight: bold; }
.elx_M { color: blue; font-weight: bold; }
.elx_R { color: blue; font-weight: bold; }
</style>
</head>
<body>
''')
w.write('<pre>')
while 1:
type, start, length = ep.get()
if type is None:
break
if cur < start:
# print out some plain text up to 'cur'
w.copy(cur, start - cur)
# wrap a bit o' formatting here
w.write('<span class="elx_%s">' % type)
# copy over the token
w.copy(start, length)
# and close up the formatting
w.write('</span>')
cur = start + length
# all done.
w.flush(cur)
w.write('</pre>')
if genpage:
w.write('</body></html>\n')
if __name__ == '__main__':
import sys
generate(open(sys.argv[1]), open(sys.argv[2]), sys.stdout, 1)

View File

@@ -1,26 +0,0 @@
#!/bin/sh
if test "$#" != 2; then
echo "USAGE: $0 SOURCE-FILE ELX-FILE"
exit 1
fi
cat <<EOF
<html><head><title>ELX Output Page</title>
<style type="text/css">
.elx_C { color: firebrick; font-style: italic; }
.elx_S { color: #bc8f8f; font-weight: bold; }
.elx_K { color: purple; font-weight: bold }
.elx_F { color: blue; font-weight: bold; }
.elx_L { color: blue; font-weight: bold; }
.elx_M { color: blue; font-weight: bold; }
.elx_R { color: blue; font-weight: bold; }
</style>
</head>
<body>
EOF
dirname="`dirname $0`"
python2 $dirname/elx_html.py $1 $2
echo "</body></html>"

View File

@@ -1,148 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include "java.h"
#include "j_keywords.h"
#include "elx.h"
/* from j_scan.c */
extern int yylex(void);
extern void yylex_start(int *error_flag);
extern void yylex_finish(void);
extern const char *get_identifier(void);
static const char *fname;
static int saw_error = 0;
static int lineno = 1;
static int hpos = 1;
static int fpos = 0;
static int token_start = 0;
static int start_lineno;
static int start_hpos;
static elx_context_t *ectx;
//#define DEBUG_SCANNER
/* if we're debugging, then the scanner looks for this var */
int yysdebug = 0;
/* and the parser looks for this */
int yydebug = 1;
void yyserror(const char *msg)
{
fprintf(stderr, "%s:%d:%d: lex error: %s\n",
fname, start_lineno, start_hpos, msg);
saw_error = 1;
}
void yyerror(const char *msg)
{
fprintf(stderr, "%s:%d:%d: parse error: %s\n",
fname, start_lineno, start_hpos, msg);
saw_error = 1;
}
int yyslex(void)
{
int c = fgetc(ectx->input_fp);
if (c == EOF)
return -1; /* tell lexer we're done */
++fpos;
if (c == '\n')
{
hpos = 1;
++lineno;
}
else
++hpos;
// printf("char: '%c'\n", c);
return c;
}
void issue_token(char which)
{
const char *ident = NULL;
if (ELX_DEFINES_SYM(which))
ident = get_identifier();
else
ident = NULL;
elx_issue_token(ectx, which, token_start, fpos - token_start + 1, ident);
}
void mark_token_start(void)
{
token_start = fpos;
start_lineno = lineno;
start_hpos = hpos;
}
#ifdef DEBUG_SCANNER
void gen_scan_tokens(void)
{
while (1)
{
int v = yylex();
if (v == TK_IDENTIFIER)
printf("%d-%d: %d '%s'\n",
token_start, fpos-1, v, get_identifier());
else
printf("%d-%d: %d\n", token_start, fpos-1, v);
/* end of parse? */
if (v <= 0)
break;
}
}
#else /* DEBUG_SCANNER */
static void gen_elx_tokens(void)
{
/* ### what to do with the result? should have seen/set saw_error */
(void) yyparse();
}
#endif /* DEBUG_SCANNER */
int main(int argc, const char **argv)
{
int errcode;
ectx = elx_process_args(argc, argv);
yylex_start(&errcode);
if (errcode)
{
fprintf(stderr, "error: yylex_start: %d\n", errcode);
return EXIT_FAILURE;
}
elx_open_files(ectx);
#ifdef DEBUG_SCANNER
gen_scan_tokens();
#else
gen_elx_tokens();
#endif
yylex_finish();
elx_close_files(ectx);
if (saw_error)
return EXIT_FAILURE;
return EXIT_SUCCESS;
}

View File

@@ -1,65 +0,0 @@
%local {
/* get the KR_* values */
#include "java.h"
}
%%
abstract
boolean
break
byte
/* byvalue */
case
/* cast */
catch
char
class
/* const */
continue
default
do
double
else
extends
false
final
finally
float
for
/* future */
/* generic */
/* goto */
if
implements
import
/* inner */
instanceof
int
interface
long
native
new
null
/* operator */
/* outer */
package
private
protected
public
/* rest */
return
short
static
super
switch
synchronized
this
throw
throws
transient
true
try
/* var */
void
volatile
while

View File

@@ -1,135 +0,0 @@
%start token
%scanner
%local {
#include "elx.h"
/* from elx-java.c */
void yyserror(const char *msg);
int yyslex(void);
/* for the TK_ symbols, generated from java.y */
#include "java.h"
/* for keyword recognition */
#include "j_keywords.h"
extern void issue_token(char which);
extern void mark_token_start(void);
#define MAX_IDENT 200
static int idlen;
static char identifier[MAX_IDENT+1];
#define INIT_IDENT(c) (identifier[0] = (c), idlen = 1)
#define ADD_IDENT(c) if (idlen == MAX_IDENT) return E_IDENT_TOO_LONG; \
else identifier[idlen++] = (c)
/* ### is there a better place? */
#define E_IDENT_TOO_LONG (-100)
static int lookup(void);
}
%%
token : pure_ws* { mark_token_start(); } slash_op
slash_op : "/=" { return TK_OPERATOR; }
| comment token
| '/' { return TK_OPERATOR; }
| one_token
|
;
one_token : t_identifier { return lookup(); }
| t_literal { return TK_LITERAL; }
| t_operator { return TK_OPERATOR; }
| t_chars { return yysprev_char; }
| t_inc_dec { return TK_INC_DEC; }
| t_bracket
;
t_identifier : alpha { INIT_IDENT(yysprev_char); }
( alphanum { ADD_IDENT(yysprev_char); } )*
alpha : 'a' - 'z' | 'A' - 'Z' | '_' | '$'
alphanum : alpha | digit
digit : '0' - '9'
hexdigit : digit | 'a' - 'f' | 'A' - 'F'
octal : '0' - '7'
t_literal : number | string | char_constant
number : ('1' - '9') digit* decimal_suffix
| '.' digit+ [exponent] [float_suffix]
| '0' (('x' | 'X') hexdigit+ | octal+) decimal_suffix
;
decimal_suffix : ('.' digit* [exponent] [float_suffix])
| 'l' | 'L'
| /* nothing */
;
exponent : ('e' | 'E') ['+' | '-'] digit+
float_suffix : 'f' | 'F' | 'd' | 'D'
string : '"' string_char* '"' { issue_token(ELX_STRING); }
string_char : '\1' -> '"' | '"' <-> '\\' | '\\' <- '\377' | '\\' '\1' - '\377'
char_constant : '\'' one_char '\''
one_char : '\1' -> '\'' | '\'' <-> '\\' | '\\' <- '\377' | '\\' '\1' - '\377'
comment : ( "//" line_comment_char* '\n'
| "/*" (block_comment_char | '*' block_non_term_char)* "*/"
) { issue_token(ELX_COMMENT); }
;
line_comment_char : '\1' -> '\n' | '\n' <- '\377'
block_comment_char : '\1' -> '*' | '*' <- '\377'
block_non_term_char : '\1' -> '/' | '/' <- '\377'
t_operator : "<<" | ">>" | ">>>"
| ">=" | "<=" | "==" | "!=" | "&&" | "||"
| "*=" | "%=" | "+=" | "-=" | "<<=" | ">>="
| ">>>=" | "&=" | "^=" | "|="
| '<' | '>' | '%' | '^' | '&' | '|'
;
t_inc_dec : "++" | "--"
/* note: could not use ws* ; the '[' form would only reduce on $end
rather than "any" character. that meant we could not recognize '['
within the program text. separating out the cases Does The Right
Thing */
t_bracket : '[' { return '['; }
| '[' ']' { return TK_DIM; }
| '[' ws+ ']' { return TK_DIM; }
;
t_chars : ',' | ';' | '.' | '{' | '}' | '=' | '(' | ')' | ':'
| ']' | '!' | '~' | '+' | '-' | '*' | '?'
;
ws : pure_ws | comment
pure_ws : ' ' | '\t' | '\n' | '\f'
%%
static int lookup(void)
{
int kw = KR_find_keyword(identifier, idlen);
if (kw == KR__not_found)
{
/* terminate so user can grab an identifier string */
identifier[idlen] = '\0';
return TK_IDENTIFIER;
}
issue_token(ELX_KEYWORD);
return kw;
}
const char *get_identifier(void)
{
return identifier;
}

View File

@@ -1,458 +0,0 @@
%token KR_abstract
%token KR_boolean KR_break KR_byte /* KR_byvalue */
%token KR_case /* KR_cast */ KR_catch KR_char KR_class /* KR_const */ KR_continue
%token KR_default KR_do KR_double
%token KR_else KR_extends
%token KR_false KR_final KR_finally KR_float KR_for /* KR_future */
/* %token KR_generic KR_goto */
%token KR_if KR_implements KR_import /* KR_inner */ KR_instanceof KR_int KR_interface
%token KR_long
%token KR_native KR_new KR_null
/* %token KR_operator KR_outer */
%token KR_package KR_private KR_protected KR_public
%token /* KR_rest */ KR_return
%token KR_short KR_static KR_super KR_switch KR_synchronized
%token KR_this KR_throw KR_throws KR_transient KR_true KR_try
%token /* KR_var */ KR_void KR_volatile
%token KR_while
%token TK_OP_ASSIGN TK_OPERATOR TK_IDENTIFIER TK_LITERAL
%token TK_DIM TK_INC_DEC
%start CompilationUnit
/* the standard if/then/else conflict */
/* %expect 1 */
%{
#include "elx.h"
void yyerror(const char *msg);
int yylex(void);
/* ### should come from an elx-python.h or something */
void issue_token(char which);
%}
%export {
/* the main parsing function */
int yyparse(void);
/* need to define the 'not found' in addition to the regular keywords */
#define KR__not_found 0
}
%%
TypeSpecifier
: TypeName
| TypeNameDims
;
TypeNameDims
: TypeName TK_DIM+
;
TypeNameDot
: NamePeriod
| PrimitiveType '.'
;
TypeName
: PrimitiveType
| NamePeriod TK_IDENTIFIER
| TK_IDENTIFIER
;
NamePeriod
: TK_IDENTIFIER '.'
| NamePeriod TK_IDENTIFIER '.'
;
TypeNameList
: TypeName / ','
;
PrimitiveType
: KR_boolean
| KR_byte
| KR_char
| KR_double
| KR_float
| KR_int
| KR_long
| KR_short
| KR_void
;
CompilationUnit
: PackageStatement [ImportStatements] [TypeDeclarations]
| ImportStatements [TypeDeclarations]
| TypeDeclarations
;
PackageStatement
: KR_package (TK_IDENTIFIER / '.') ';'
;
TypeDeclarations
: TypeDeclaration+
;
TypeDeclaration
: ClassDeclaration
| InterfaceDeclaration
;
ImportStatements
: ImportStatement+
;
ImportStatement
: KR_import TK_IDENTIFIER ('.' TK_IDENTIFIER)* [".*"] ';'
;
/*
QualifiedName
: TK_IDENTIFIER / '.'
;
*/
ClassDeclaration
: [Modifiers] KR_class TK_IDENTIFIER [Super] [Interfaces] ClassBody
;
Modifiers
: Modifier+
;
Modifier
: KR_abstract
| KR_final
| KR_public
| KR_protected
| KR_private
| KR_static
| KR_transient
| KR_volatile
| KR_native
| KR_synchronized
;
Super
: KR_extends TypeNameList
;
Interfaces
: KR_implements TypeNameList
;
ClassBody
: '{' FieldDeclaration* '}'
;
FieldDeclaration
: FieldVariableDeclaration
| MethodDeclaration
| ConstructorDeclaration
| StaticInitializer
;
FieldVariableDeclaration
: [Modifiers] TypeSpecifier VariableDeclarators ';'
;
VariableDeclarators
: VariableDeclarator / ','
;
VariableDeclarator
: DeclaratorName ['=' VariableInitializer]
;
VariableInitializer
: Expression
| '{' [ArrayInitializers] '}'
;
ArrayInitializers
: VariableInitializer ( ',' [VariableInitializer] )*
;
MethodDeclaration
: [Modifiers] TypeSpecifier MethodDeclarator [Throws] MethodBody
;
MethodDeclarator
: DeclaratorName '(' [ParameterList] ')' TK_DIM*
;
ParameterList
: Parameter / ','
;
Parameter
: TypeSpecifier DeclaratorName
;
DeclaratorName
: TK_IDENTIFIER TK_DIM*
;
Throws
: KR_throws TypeNameList
;
MethodBody
: Block
| ';'
;
ConstructorDeclaration
: [Modifiers] ConstructorDeclarator [Throws] Block
;
ConstructorDeclarator
: TypeName '(' [ParameterList] ')'
;
StaticInitializer
: KR_static Block
;
InterfaceDeclaration
: [Modifiers] KR_interface TK_IDENTIFIER [ExtendsInterfaces] InterfaceBody
;
ExtendsInterfaces
: KR_extends TypeNameList
;
InterfaceBody
: '{' FieldDeclaration+ '}'
;
Block
: '{' LocalVariableDeclarationOrStatement* '}'
;
LocalVariableDeclarationOrStatement
: LocalVariableDeclarationStatement
| Statement
;
LocalVariableDeclarationStatement
: TypeSpecifier VariableDeclarators ';'
;
Statement
: EmptyStatement
| LabeledStatement
| ExpressionStatement ';'
| SelectionStatement
| IterationStatement
| JumpStatement
| GuardingStatement
| Block
;
EmptyStatement
: ';'
;
LabeledStatement
: TK_IDENTIFIER ':' LocalVariableDeclarationOrStatement
| KR_case ConstantExpression ':' LocalVariableDeclarationOrStatement
| KR_default ':' LocalVariableDeclarationOrStatement
;
ExpressionStatement
: Expression
;
SelectionStatement
: KR_if '(' Expression ')' Statement [KR_else Statement]
| KR_switch '(' Expression ')' Block
;
IterationStatement
: KR_while '(' Expression ')' Statement
| KR_do Statement KR_while '(' Expression ')' ';'
| KR_for '(' ForInit ForExpr [ForIncr] ')' Statement
;
ForInit
: ExpressionStatements ';'
| LocalVariableDeclarationStatement
| ';'
;
ForExpr
: [Expression] ';'
;
ForIncr
: ExpressionStatements
;
ExpressionStatements
: ExpressionStatement / ','
;
JumpStatement
: KR_break [TK_IDENTIFIER] ';'
| KR_continue [TK_IDENTIFIER] ';'
| KR_return [Expression] ';'
| KR_throw Expression ';'
;
GuardingStatement
: KR_synchronized '(' Expression ')' Statement
| KR_try Block Finally
| KR_try Block Catches
| KR_try Block Catches Finally
;
Catches
: Catch+
;
Catch
: KR_catch '(' TypeSpecifier [TK_IDENTIFIER] ')' Block
;
Finally
: KR_finally Block
;
ArgumentList
: Expression / ','
;
PrimaryExpression
: TK_LITERAL
| KR_true | KR_false
| KR_this
| KR_null
| KR_super
| '(' Expression ')'
;
PostfixExpression
: PrimaryExpression Trailers
| TypeName AltTrailers
| TypeNameDot FollowsPeriod Trailers
| TypeNameDot DimAllocation TypeTrailers
| TypeNameDims TypeTrailers
| KR_new TypeName AltTrailers
| KR_new TypeNameDims TypeTrailers
;
DimAllocation
: KR_new TypeNameDims
;
PostfixDims
: TK_DIM+ '.' KR_class
;
FollowsPeriod
: KR_this
| KR_class
| KR_super
| KR_new TypeName NoPeriodsTrailer
;
NoPeriodsTrailer
: '[' Expression ']'
| '(' [ArgumentList] ')'
;
NoDimTrailer
: NoPeriodsTrailer
| '.' (FollowsPeriod | TK_IDENTIFIER)
;
AnyTrailer
: NoDimTrailer
| PostfixDims
;
Trailers
: (AnyTrailer | DimAllocation NoDimTrailer)* [DimAllocation]
;
AltTrailers
: NoPeriodsTrailer Trailers
|
;
TypeTrailers
: NoDimTrailer Trailers
|
;
CastablePrefixExpression
: PostfixExpression [TK_INC_DEC]
| LogicalUnaryOperator CastExpression
;
LogicalUnaryOperator
: '~'
| '!'
;
UnaryOperator
: '+'
| '-'
| TK_INC_DEC
;
/* note: we don't actually have grammar for a cast. we just rely on:
(expr) (argument)
as our parse match */
CastExpression
: /* '(' PrimitiveType ')' CastExpression
| '(' NamePeriod TK_IDENTIFIER TK_DIM TK_DIM* ')' CastablePrefixExpression
| '(' NamePeriod TK_IDENTIFIER ')' CastablePrefixExpression
| '(' TK_IDENTIFIER TK_DIM TK_DIM* ')' CastablePrefixExpression
| '(' TK_IDENTIFIER ')' CastablePrefixExpression
| */ UnaryOperator CastExpression
| CastablePrefixExpression
;
BinaryExpression
: CastExpression
| BinaryExpression TK_BINARY CastExpression
| BinaryExpression KR_instanceof TypeSpecifier
;
ConditionalExpression
: BinaryExpression
| BinaryExpression '?' Expression ':' ConditionalExpression
;
AssignmentExpression
: ConditionalExpression [AssignmentOperator AssignmentExpression]
;
AssignmentOperator
: '='
| TK_OP_ASSIGN
;
Expression
: AssignmentExpression
;
ConstantExpression
: ConditionalExpression
;
/*
TK_OPERATOR : OP_LOR | OP_LAND
| OP_EQ | OP_NE | OP_LE | OP_GE
| OP_SHL | OP_SHR | OP_SHRR
;
*/
TK_BINARY : TK_OPERATOR | '+' | '-' | '*'

View File

@@ -1,150 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include "scanner.h"
#include "python.h"
#include "py_keywords.h"
#include "elx.h"
extern int yylex(void);
static const char *fname;
static int saw_error = 0;
static void *scan_ctx;
static elx_context_t *ectx;
void yyerror(const char *msg)
{
int sl, sc, el, ec;
scanner_token_linecol(scan_ctx, &sl, &sc, &el, &ec);
fprintf(stderr, "%s:%d:%d: parse error: %s\n", fname, sl, sc, msg);
saw_error = 1;
}
int reader(void *user_ctx)
{
FILE *inf = user_ctx;
int c = fgetc(inf);
if (c == EOF)
return SCANNER_EOF;
// printf("char: '%c'\n", c);
return c;
}
void issue_token(char which)
{
int start;
int end;
const char *ident = NULL;
scanner_token_range(scan_ctx, &start, &end);
if (ELX_DEFINES_SYM(which))
{
int length;
scanner_identifier(scan_ctx, &ident, &length);
}
elx_issue_token(ectx, which, start, end - start + 1, ident);
}
int yylex(void)
{
int v;
do {
v = scanner_get_token(scan_ctx);
if (v == TK_COMMENT)
{
issue_token(ELX_COMMENT);
}
} while (v == TK_COMMENT);
/* is this identifier a keyword? */
if (v == TK_IDENTIFIER)
{
const char *ident;
int length;
int kw;
scanner_identifier(scan_ctx, &ident, &length);
#if 0
printf("id=%s\n", ident);
#endif
kw = KR_find_keyword(ident, length);
if (kw != KR__not_found)
{
v = kw;
issue_token(ELX_KEYWORD);
}
}
else if (v == TK_STRING)
{
issue_token(ELX_STRING);
}
// printf("token=%d\n", v);
return v;
}
#ifdef DEBUG_SCANNER
void gen_scan_tokens(void)
{
while (1)
{
int v = scanner_get_token(scan_ctx);
int sl, sc, el, ec;
scanner_token_linecol(scan_ctx, &sl, &sc, &el, &ec);
if (v == TK_NEWLINE)
printf("%d,%d: NEWLINE\n", sl, sc);
else if (v == TK_INDENT)
printf("%d,%d: INDENT\n", el, ec);
else if (v == TK_DEDENT)
printf("%d,%d: DEDENT\n", el, ec);
else
printf("%d,%d-%d,%d: %d\n", sl, sc, el, ec, v);
/* end of parse? */
if (v <= 0)
break;
}
}
#endif /* DEBUG_SCANNER */
static void gen_elx_tokens(void)
{
/* ### what to do with the result? should have seen/set saw_error */
(void) yyparse();
}
int main(int argc, const char **argv)
{
ectx = elx_process_args(argc, argv);
elx_open_files(ectx);
scan_ctx = scanner_begin(reader, ectx->input_fp);
#ifdef DEBUG_SCANNER
gen_scan_tokens();
#else
gen_elx_tokens();
#endif
scanner_end(scan_ctx);
elx_close_files(ectx);
if (saw_error)
return EXIT_FAILURE;
return EXIT_SUCCESS;
}

View File

@@ -1,36 +0,0 @@
%local {
/* get the KR_* values */
#include "python.h"
}
%%
and
/* as */
assert
break
class
continue
def
del
elif
else
except
exec
finally
for
from
global
if
import
in
is
lambda
not
or
pass
print
raise
return
try
while
yield

View File

@@ -1,135 +0,0 @@
%token TK_COMMENT TK_IDENTIFIER TK_NUMBER
%token TK_OPERATOR TK_STRING
%token TK_INDENT TK_DEDENT TK_NEWLINE
%token KR_and KR_assert KR_break KR_class KR_continue KR_def
%token KR_del KR_elif KR_else KR_except KR_exec KR_finally
%token KR_for KR_from KR_global KR_if KR_import KR_in KR_is
%token KR_lambda KR_not KR_or KR_pass KR_print KR_raise
%token KR_return KR_try KR_while KR_yield
%start file_input
%{
#include "elx.h"
void yyerror(const char *msg);
int yylex(void);
/* ### should come from an elx-python.h or something */
void issue_token(char which);
%}
%export {
/* the main parsing function */
int yyparse(void);
/* need to define the 'not found' in addition to the regular keywords */
#define KR__not_found 0
}
%%
file_input: (TK_NEWLINE | stmt)*
NAME: TK_IDENTIFIER
funcdef: KR_def NAME { issue_token(ELX_LOCAL_FDEF); } parameters ':' suite
parameters: '(' [varargslist] ')'
varargslist: paramdef (',' paramdef)* [',' [varargsdef]]
| varargsdef
;
/* the TK_OPERATOR represents '*' or '**' */
varargsdef: TK_OPERATOR NAME [',' TK_OPERATOR NAME]
paramdef: fpdef [TK_OPERATOR test]
fpdef: NAME | '(' fplist ')'
fplist: fpdef (',' fpdef)* [',']
stmt: simple_stmt | compound_stmt
simple_stmt: small_stmt (';' small_stmt)* [';'] TK_NEWLINE
small_stmt: expr_stmt | print_stmt | raise_stmt
| import_stmt | global_stmt | exec_stmt | assert_stmt
| KR_del exprlist
| KR_pass
| KR_break
| KR_continue
| KR_return [testlist]
| KR_yield testlist
;
/* expr_stmt is normally assignment, which we get thru TK_OPERATOR in 'expr' */
expr_stmt: testlist
/* a print normally allows '>> test'; since that is a TK_OPERATOR, we
get it as part of 'factor'. this rule also allows for a trailing
comma in '>> test,' which the normal print doesn't */
print_stmt: KR_print [test (',' test)* [',']]
raise_stmt: KR_raise [test [',' test [',' test]]]
/* the TK_OPERATOR represents '*' */
import_stmt: KR_import dotted_as_name (',' dotted_as_name)*
| KR_from dotted_name KR_import (TK_OPERATOR | import_as_name (',' import_as_name)*)
import_as_name: NAME [NAME NAME]
dotted_as_name: dotted_name [NAME NAME]
dotted_name: NAME ('.' NAME)*
global_stmt: KR_global NAME (',' NAME)*
exec_stmt: KR_exec expr [KR_in test [',' test]]
assert_stmt: KR_assert test [',' test]
compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | funcdef | classdef
if_stmt: KR_if test ':' suite (KR_elif test ':' suite)* [KR_else ':' suite]
while_stmt: KR_while test ':' suite [KR_else ':' suite]
for_stmt: KR_for exprlist KR_in testlist ':' suite [KR_else ':' suite]
try_stmt: KR_try ':' suite (except_clause ':' suite)+
[KR_else ':' suite] | KR_try ':' suite KR_finally ':' suite
/* NB compile.c makes sure that the default except clause is last */
except_clause: KR_except [test [',' test]]
suite: simple_stmt | TK_NEWLINE TK_INDENT stmt+ TK_DEDENT
test: test_factor (test_op test_factor | KR_is [KR_not] factor)*
[TK_OPERATOR lambdef] | lambdef
test_op: bin_op | KR_in
test_factor: KR_not* factor
expr: factor (expr_op factor)*
expr_op: bin_op | KR_is [KR_not]
factor: TK_OPERATOR* atom trailer*
bin_op: TK_OPERATOR | KR_or | KR_and | KR_not KR_in
atom: '(' [testlist] ')' | '[' [listmaker] ']' | '{' [dictmaker] '}'
| '`' testlist_no_trailing '`' | TK_IDENTIFIER | TK_NUMBER | TK_STRING+
listmaker: test ( list_for | (',' test)* [','] )
lambdef: KR_lambda [varargslist] ':' test
trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME
subscriptlist: subscript (',' subscript)* [',']
subscript: '.' '.' '.' | test | [test] ':' [test] [sliceop]
sliceop: ':' [test]
exprlist: expr (',' expr)* [',']
testlist: test (',' test)* [',']
testlist_no_trailing: test (',' test)*
testlist_safe: test [(',' test)+ [',']] /* doesn't match: test, */
dictmaker: test ':' test (',' test ':' test)* [',']
classdef: KR_class NAME ['(' testlist ')'] ':' suite
/* arguments are normally 'keyword = test; since '=' is TK_OPERATOR, we
match keyword arguments as part of 'test' (in 'expr').
the vararg portion is normally '* test' or '** test'; since '*' and
'**' are TK_OPERATOR, we match varargs as part of 'test' (in
'factor')
thus, all argument forms are simply 'test'
varargs does not normally allow a trailing comma, but we can
simplify things and allow a match
*/
arglist: test (',' test)* [',']
list_iter: list_for | list_if
list_for: KR_for exprlist KR_in testlist_safe [list_iter]
list_if: KR_if test [list_iter]

View File

@@ -1,523 +0,0 @@
#include <stdlib.h>
#include <ctype.h>
#include <assert.h>
#include <string.h>
#include "python.h" /* get the TK_ values */
#include "scanner.h"
#define SCANNER_EMPTY (SCANNER_EOF - 1) /* -2 */
#define SCANNER_TABSIZE 8
#define SCANNER_MAXINDENT 100
#define SCANNER_MAXIDLEN 200
typedef struct
{
get_char_t getfunc;
void *user_ctx;
char saved;
int was_newline; /* was previous character a newline? */
int start; /* start position of last token returned */
int start_col;
int start_line;
int fpos; /* file position */
int lineno; /* file line number */
int line_pos; /* file position of current line's first char */
int nesting_level;
int indent; /* which indent */
int indents[SCANNER_MAXINDENT]; /* the set of indents */
int dedent_count; /* how many DEDENTs to issue */
int skip_newline; /* skip the newline after a blank_line + comment */
int idlen;
char identifier[SCANNER_MAXIDLEN]; /* accumulated identifier */
} scanner_ctx;
static int next_char(scanner_ctx *ctx)
{
int c;
++ctx->fpos;
if (ctx->saved == SCANNER_EMPTY)
{
return (*ctx->getfunc)(ctx->user_ctx);
}
c = ctx->saved;
ctx->saved = SCANNER_EMPTY;
return c;
}
static void backup_char(scanner_ctx *ctx, int c)
{
assert(ctx->saved == SCANNER_EMPTY);
ctx->saved = c;
ctx->was_newline = 0; /* we may have put it back */
--ctx->fpos;
}
/* called to note that we've moved on to another line */
static void on_next_line(scanner_ctx *ctx)
{
ctx->line_pos = ctx->fpos;
++ctx->lineno;
}
void *scanner_begin(get_char_t getfunc, void *user_ctx)
{
scanner_ctx *ctx = malloc(sizeof(*ctx));
memset(ctx, 0, sizeof(*ctx));
ctx->getfunc = getfunc;
ctx->user_ctx = user_ctx;
ctx->saved = SCANNER_EMPTY;
ctx->lineno = 1;
return ctx;
}
int scanner_get_token(void *opaque_ctx)
{
scanner_ctx *ctx = opaque_ctx;
int c;
int c2;
int blank_line;
if (ctx->dedent_count)
{
--ctx->dedent_count;
return TK_DEDENT;
}
nextline:
blank_line = 0;
/* if we're at the start of the line, then get the indentation level */
if (ctx->fpos == ctx->line_pos)
{
int col = 0;
while (1)
{
c = next_char(ctx);
if (c == ' ')
++col;
else if (c == '\t')
col = (col / SCANNER_TABSIZE + 1) * SCANNER_TABSIZE;
else if (c == '\f') /* ^L / formfeed */
col = 0;
else
break;
}
backup_char(ctx, c);
if (c == '#' || c == '\n')
{
/* this is a "blank" line and doesn't count towards indentation,
and it doesn't produce NEWLINE tokens */
blank_line = 1;
}
/* if it isn't blank, and we aren't inside nesting expressions, then
we need to handle INDENT/DEDENT */
if (!blank_line && ctx->nesting_level == 0)
{
int last_indent = ctx->indents[ctx->indent];
if (col == last_indent)
{
/* no change */
}
else if (col > last_indent)
{
if (ctx->indent == SCANNER_MAXINDENT - 1)
{
/* oops. too deep. */
return E_TOO_MANY_INDENTS;
}
ctx->indents[++ctx->indent] = col;
return TK_INDENT;
}
else /* col < last_indent */
{
/* find the previous indentation that matches this one */
while (ctx->indent > 0
&& col < ctx->indents[ctx->indent])
{
++ctx->dedent_count;
--ctx->indent;
}
if (col != ctx->indents[ctx->indent])
{
/* oops. dedent doesn't match any indent. */
return E_DEDENT_MISMATCH;
}
/* deliver one dedent now */
--ctx->dedent_count;
return TK_DEDENT;
}
} /* !blank_line ... */
} /* start of line */
/* start here if we see a line continuation */
read_more:
do {
c = next_char(ctx);
} while (c == ' ' || c == '\t' || c == '\f');
/* here is where the token starts */
ctx->start = ctx->fpos;
ctx->start_line = ctx->lineno;
ctx->start_col = ctx->fpos - ctx->line_pos;
/* comment? */
if (c == '#')
{
do {
c = next_char(ctx);
} while (c != SCANNER_EOF && c != '\n');
/* if we are suppressing newlines because this is a blank line, then
leave a marker to skip the newline, next time through. */
if (blank_line && c == '\n')
ctx->skip_newline = 1;
/* put back whatever we sucked up */
backup_char(ctx, c);
return TK_COMMENT;
}
/* Look for an identifier */
if (isalpha(c) || c == '_')
{
ctx->idlen = 0;
/* is this actually a string? */
if (c == 'r' || c == 'R')
{
ctx->identifier[ctx->idlen++] = c;
c = next_char(ctx);
if (c == '"' || c == '\'')
goto parse_string;
}
else if (c == 'u' || c == 'U')
{
ctx->identifier[ctx->idlen++] = c;
c = next_char(ctx);
if (c == 'r' || c == 'R')
{
ctx->identifier[ctx->idlen++] = c;
c = next_char(ctx);
}
if (c == '"' || c == '\'')
goto parse_string;
}
while (isalnum(c) || c == '_') {
/* store the character if there is room for it, and room left
for a null-terminator. */
if (ctx->idlen < SCANNER_MAXIDLEN-1)
ctx->identifier[ctx->idlen++] = c;
c = next_char(ctx);
}
backup_char(ctx, c);
/* ### check for a keyword */
return TK_IDENTIFIER;
}
if (c == '\n')
{
on_next_line(ctx);
/* don't report NEWLINE tokens for blank lines or nested exprs */
if (blank_line || ctx->nesting_level > 0 || ctx->skip_newline)
{
ctx->skip_newline = 0;
goto nextline;
}
return TK_NEWLINE;
}
if (c == '.')
{
c = next_char(ctx);
if (isdigit(c))
goto parse_fraction;
backup_char(ctx, c);
return '.';
}
if (isdigit(c))
{
if (c == '0')
{
c = next_char(ctx);
if (c == 'x' || c == 'X')
{
do {
c = next_char(ctx);
} while (isxdigit(c));
goto skip_fp;
}
else if (isdigit(c))
{
do {
c = next_char(ctx);
} while (isdigit(c));
}
if (c == '.')
goto parse_fraction;
if (c == 'e' || c == 'E')
goto parse_exponent;
if (c == 'j' || c == 'J')
goto parse_imaginary;
skip_fp:
/* this point: parsed an octal, decimal, or hexadecimal */
if (c == 'l' || c == 'L')
{
/* we consumed just enough. stop and return a NUMBER */
return TK_NUMBER;
}
/* consumed too much. backup and return a NUMBER */
backup_char(ctx, c);
return TK_NUMBER;
}
/* decimal number */
do {
c = next_char(ctx);
} while (isdigit(c));
if (c == 'l' || c == 'L')
{
/* we consumed just enogh. stop and return a NUMBER */
return TK_NUMBER;
}
if (c == '.')
{
parse_fraction:
do {
c = next_char(ctx);
} while (isdigit(c));
}
if (c == 'e' || c == 'E')
{
parse_exponent:
c = next_char(ctx);
if (c == '+' || c == '-')
c = next_char(ctx);
if (!isdigit(c))
{
backup_char(ctx, c);
return E_BAD_NUMBER;
}
do {
c = next_char(ctx);
} while (isdigit(c));
}
if (c == 'j' || c == 'J')
{
parse_imaginary:
c = next_char(ctx);
}
/* one too far. backup and return a NUMBER */
backup_char(ctx, c);
return TK_NUMBER;
} /* isdigit */
parse_string:
if (c == '\'' || c == '"')
{
int second_quote_pos = ctx->fpos + 1;
int which_quote = c;
int is_triple = 0;
int quote_count = 0;
while (1)
{
c = next_char(ctx);
if (c == '\n')
{
on_next_line(ctx);
if (!is_triple)
return E_UNTERM_STRING;
quote_count = 0;
}
else if (c == SCANNER_EOF)
{
return E_UNTERM_STRING;
}
else if (c == which_quote)
{
++quote_count;
if (ctx->fpos == second_quote_pos)
{
c = next_char(ctx);
if (c == which_quote)
{
is_triple = 1;
quote_count = 0;
continue;
}
/* we just read one past the empty string. back up. */
backup_char(ctx, c);
}
/* this quote may have terminated the string */
if (!is_triple || quote_count == 3)
return TK_STRING;
}
else if (c == '\\')
{
c = next_char(ctx);
if (c == SCANNER_EOF)
return E_UNTERM_STRING;
if (c == '\n')
on_next_line(ctx);
quote_count = 0;
}
else
{
quote_count = 0;
}
}
/* NOTREACHED */
}
/* line continuation */
if (c == '\\')
{
c = next_char(ctx);
if (c != '\n')
return E_BAD_CONTINUATION;
on_next_line(ctx);
goto read_more;
}
/* look for operators */
/* the nesting operators */
if (c == '(' || c == '[' || c == '{')
{
++ctx->nesting_level;
return c;
}
if (c == ')' || c == ']' || c == '}')
{
--ctx->nesting_level;
return c;
}
/* look for up-to-3-char ops */
if (c == '<' || c == '>' || c == '*' || c == '/')
{
c2 = next_char(ctx);
if (c == c2)
{
c2 = next_char(ctx);
if (c2 != '=')
{
/* oops. one too far. */
backup_char(ctx, c2);
}
return TK_OPERATOR;
}
if (c == '<' && c2 == '>')
return TK_OPERATOR;
if (c2 != '=')
{
/* one char too far. */
backup_char(ctx, c2);
}
return TK_OPERATOR;
}
/* look for 2-char ops */
if (c == '=' || c == '!' || c == '+' || c == '-'
|| c == '|' || c == '%' || c == '&' || c == '^')
{
c2 = next_char(ctx);
if (c2 == '=')
return TK_OPERATOR;
/* oops. too far. */
backup_char(ctx, c2);
return TK_OPERATOR;
}
/* ### should all of these return 'c' ? */
if (c == ':' || c == ',' || c == ';' || c == '`')
return c;
/* as a unary operator, this must be a TK_OPERATOR */
if (c == '~')
return TK_OPERATOR;
/* if we have an EOF, then just return it */
if (c == SCANNER_EOF)
return SCANNER_EOF;
/* unknown input */
return E_UNKNOWN_TOKEN;
}
void scanner_identifier(void *opaque_ctx, const char **ident, int *len)
{
scanner_ctx *ctx = opaque_ctx;
ctx->identifier[ctx->idlen] = '\0';
*ident = ctx->identifier;
*len = ctx->idlen;
}
void scanner_token_range(void *opaque_ctx, int *start, int *end)
{
scanner_ctx *ctx = opaque_ctx;
*start = ctx->start;
*end = ctx->fpos;
}
void scanner_token_linecol(void *opaque_ctx,
int *sline, int *scol, int *eline, int *ecol)
{
scanner_ctx *ctx = opaque_ctx;
*sline = ctx->start_line;
*scol = ctx->start_col;
*eline = ctx->lineno;
*ecol = ctx->fpos - ctx->line_pos;
}
void scanner_end(void *ctx)
{
free(ctx);
}

View File

@@ -1,42 +0,0 @@
#ifndef SCANNER_H
#define SCANNER_H
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/* constants and errors returned by the scanner */
enum
{
SCANNER_EOF = -1, /* returned by get_char_t and
scanner_get_token to symbolize EOF */
E_TOO_MANY_INDENTS = -100, /* too many indents */
E_DEDENT_MISMATCH, /* no matching indent */
E_BAD_CONTINUATION, /* character occurred after \ */
E_BAD_NUMBER, /* parse error in a number */
E_UNKNOWN_TOKEN, /* dunno what we found */
E_UNTERM_STRING /* unterminated string constant */
};
typedef int (*get_char_t)(void *user_ctx);
void *scanner_begin(get_char_t getfunc, void *user_ctx);
int scanner_get_token(void *ctx);
void scanner_identifier(void *ctx, const char **ident, int *len);
void scanner_token_range(void *ctx, int *start, int *end);
void scanner_token_linecol(void *ctx,
int *sline, int *scol, int *eline, int *ecol);
void scanner_end(void *ctx);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* SCANNER_H */

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python
# -*-python-*-
#
# Copyright (C) 1999-2010 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2008 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
@@ -32,8 +32,9 @@ import os
import re
import time
import math
import cgi
import vclib
import sapi
re_includes = re.compile('\\#(\\s*)include(\\s*)"(.*?)"')
@@ -81,7 +82,7 @@ class HTMLBlameSource:
diff_url = None
if item.prev_rev:
diff_url = '%sr1=%s&amp;r2=%s' % (self.diff_url, item.prev_rev, item.rev)
thisline = link_includes(sapi.escape(item.text), self.repos,
thisline = link_includes(cgi.escape(item.text), self.repos,
self.path_parts, self.include_url)
return _item(text=thisline, line_number=item.line_number,
rev=item.rev, prev_rev=item.prev_rev,

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2010 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2009 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC
@@ -19,154 +19,82 @@ import os
import string
import ConfigParser
import fnmatch
import vclib
import vclib.ccvs
import vclib.svn
import cvsdb
#########################################################################
#
# CONFIGURATION
# -------------
#
# There are three forms of configuration:
#
# 1. edit the viewvc.conf created by the viewvc-install(er)
# 2. as (1), but delete all unchanged entries from viewvc.conf
# 3. do not use viewvc.conf and just edit the defaults in this file
# 1) edit the viewvc.conf created by the viewvc-install(er)
# 2) as (1), but delete all unchanged entries from viewvc.conf
# 3) do not use viewvc.conf and just edit the defaults in this file
#
# Most users will want to use (1), but there are slight speed advantages
# to the other two options. Note that viewvc.conf values are a bit easier
# to work with since it is raw text, rather than python literal values.
#
#
# A WORD ABOUT OPTION LAYERING/OVERRIDES
# --------------------------------------
#
# ViewVC has three "layers" of configuration options:
#
# 1. base configuration options - very basic configuration bits
# found in sections like 'general', 'options', etc.
# 2. vhost overrides - these options overlay/override the base
# configuration on a per-vhost basis.
# 3. root overrides - these options overlay/override the base
# configuration and vhost overrides on a per-root basis.
#
# Here's a diagram of the valid overlays/overrides:
#
# PER-ROOT PER-VHOST BASE
#
# ,-----------. ,-----------.
# | vhost-*/ | | |
# | general | --> | general |
# | | | |
# `-----------' `-----------'
# ,-----------. ,-----------. ,-----------.
# | root-*/ | | vhost-*/ | | |
# | options | --> | options | --> | options |
# | | | | | |
# `-----------' `-----------' `-----------'
# ,-----------. ,-----------. ,-----------.
# | root-*/ | | vhost-*/ | | |
# | templates | --> | templates | --> | templates |
# | | | | | |
# `-----------' `-----------' `-----------'
# ,-----------. ,-----------. ,-----------.
# | root-*/ | | vhost-*/ | | |
# | utilities | --> | utilities | --> | utilities |
# | | | | | |
# `-----------' `-----------' `-----------'
# ,-----------. ,-----------.
# | vhost-*/ | | |
# | cvsdb | --> | cvsdb |
# | | | |
# `-----------' `-----------'
# ,-----------. ,-----------. ,-----------.
# | root-*/ | | vhost-*/ | | |
# | authz-* | --> | authz-* | --> | authz-* |
# | | | | | |
# `-----------' `-----------' `-----------'
# ,-----------.
# | |
# | vhosts |
# | |
# `-----------'
# ,-----------.
# | |
# | query |
# | |
# `-----------'
#
# ### TODO: Figure out what this all means for the 'kv' stuff.
#
#########################################################################
class Config:
_base_sections = (
# Base configuration sections.
'authz-*',
'cvsdb',
'general',
'options',
'query',
'templates',
'utilities',
)
_force_multi_value = (
# Configuration values with multiple, comma-separated values.
'allowed_views',
'cvs_roots',
'kv_files',
'languages',
'mime_types_files',
'root_parents',
'svn_roots',
)
_allowed_overrides = {
# Mapping of override types to allowed overridable sections.
'vhost' : ('authz-*',
'cvsdb',
'general',
'options',
'templates',
'utilities',
),
'root' : ('authz-*',
'options',
'templates',
'utilities',
)
}
_sections = ('general', 'utilities', 'options', 'cvsdb', 'templates')
_force_multi_value = ('cvs_roots', 'svn_roots', 'languages', 'kv_files',
'root_parents', 'allowed_views', 'mime_types_files')
def __init__(self):
self.root_options_overlayed = 0
for section in self._base_sections:
if section[-1] == '*':
continue
for section in self._sections:
setattr(self, section, _sub_config())
def load_config(self, pathname, vhost=None):
"""Load the configuration file at PATHNAME, applying configuration
settings there as overrides to the built-in default values. If
VHOST is provided, also process the configuration overrides
specific to that virtual host."""
def load_config(self, pathname, vhost=None, rootname=None):
self.conf_path = os.path.isfile(pathname) and pathname or None
self.base = os.path.dirname(pathname)
self.parser = ConfigParser.ConfigParser()
self.parser.optionxform = lambda x: x # don't case-normalize option names.
self.parser.read(self.conf_path or [])
for section in self.parser.sections():
if self._is_allowed_section(self.parser, section,
self._base_sections):
for section in self._sections:
if self.parser.has_section(section):
self._process_section(self.parser, section, section)
if vhost and self.parser.has_section('vhosts'):
self._process_vhost(self.parser, vhost)
if rootname:
self._process_root_options(self.parser, rootname)
self.expand_root_parents()
cvsdb.setencs(self.options.encodings.split(':'))
def expand_root_parents(self):
"""Expand the configured root parents into individual roots."""
# Each item in root_parents is a "directory : repo_type" string.
for pp in self.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 self.options.hide_cvsroot and roots.has_key('CVSROOT'):
del roots['CVSROOT']
self.general.cvs_roots.update(roots)
elif repo_type == 'svn':
roots = vclib.svn.expand_root_parent(pp)
self.general.svn_roots.update(roots)
else:
raise debug.ViewVCException(
"The path '%s' in 'root_parents' has an unrecognized "
"repository type." % (pp))
def load_kv_files(self, language):
"""Process the key/value (kv) files specified in the
configuration, merging their values into the configuration as
dotted heirarchical items."""
kv = _sub_config()
for fname in self.general.kv_files:
@@ -179,7 +107,6 @@ class Config:
fname = string.replace(fname, '%lang%', language)
parser = ConfigParser.ConfigParser()
parser.optionxform = lambda x: x # don't case-normalize option names.
parser.read(os.path.join(self.base, fname))
for section in parser.sections():
for option in parser.options(section):
@@ -197,12 +124,10 @@ class Config:
return kv
def path(self, path):
"""Return PATH relative to the config file directory."""
"""Return path relative to the config file directory"""
return os.path.join(self.base, path)
def _process_section(self, parser, section, subcfg_name):
if not hasattr(self, subcfg_name):
setattr(self, subcfg_name, _sub_config())
sc = getattr(self, subcfg_name)
for opt in parser.options(section):
@@ -215,55 +140,25 @@ class Config:
except ValueError:
pass
### FIXME: This feels like unnecessary depth of knowledge for a
### semi-generic configuration object.
if opt == 'cvs_roots' or opt == 'svn_roots':
value = _parse_roots(opt, value)
setattr(sc, opt, value)
def _is_allowed_section(self, parser, section, allowed_sections):
"""Return 1 iff SECTION is an allowed section, defined as being
explicitly present in the ALLOWED_SECTIONS list or present in the
form 'someprefix-*' in that list."""
for allowed_section in allowed_sections:
if allowed_section[-1] == '*':
if _startswith(section, allowed_section[:-1]):
return 1
elif allowed_section == section:
return 1
return 0
def _is_allowed_override(self, parser, sectype, secspec, section):
"""Test if SECTION is an allowed override section for sections of
type SECTYPE ('vhosts' or 'root', currently) and type-specifier
SECSPEC (a rootname or vhostname, currently). If it is, return
the overridden base section name. If it's not an override section
at all, return None. And if it's an override section but not an
allowed one, raise IllegalOverrideSection."""
cv = '%s-%s/' % (sectype, secspec)
lcv = len(cv)
if section[:lcv] != cv:
return None
base_section = section[lcv:]
if self._is_allowed_section(parser, base_section,
self._allowed_overrides[sectype]):
return base_section
raise IllegalOverrideSection(sectype, section)
def _process_vhost(self, parser, vhost):
# Find a vhost name for this VHOST, if any (else, we've nothing to do).
# find a vhost name for this vhost, if any (if not, we've nothing to do)
canon_vhost = self._find_canon_vhost(parser, vhost)
if not canon_vhost:
return
# Overlay any option sections associated with this vhost name.
# overlay any option sections associated with this vhost name
cv = 'vhost-%s/' % (canon_vhost)
lcv = len(cv)
for section in parser.sections():
base_section = self._is_allowed_override(parser, 'vhost',
canon_vhost, section)
if base_section:
if section[:lcv] == cv:
base_section = section[lcv:]
if base_section not in self._sections:
raise IllegalOverrideSection('vhost', section)
self._process_section(parser, section, base_section)
def _find_canon_vhost(self, parser, vhost):
@@ -278,30 +173,26 @@ class Config:
return None
def _process_root_options(self, parser, rootname):
rn = 'root-%s/' % (rootname)
lrn = len(rn)
for section in parser.sections():
if section[:lrn] == rn:
base_section = section[lrn:]
if base_section in self._sections:
if base_section == 'general':
raise IllegalOverrideSection('root', section)
self._process_section(parser, section, base_section)
elif _startswith(base_section, 'authz-'):
pass
else:
raise IllegalOverrideSection('root', section)
def overlay_root_options(self, rootname):
"""Overlay per-root options for ROOTNAME atop the existing option
set. This is a destructive change to the configuration."""
did_overlay = 0
"Overly per-root options atop the existing option set."
if not self.conf_path:
return
for section in self.parser.sections():
base_section = self._is_allowed_override(self.parser, 'root',
rootname, section)
if base_section:
# We can currently only deal with root overlays happening
# once, so check that we've not yet done any overlaying of
# per-root options.
assert(self.root_options_overlayed == 0)
self._process_section(self.parser, section, base_section)
did_overlay = 1
# If we actually did any overlaying, remember this fact so we
# don't do it again later.
if did_overlay:
self.root_options_overlayed = 1
self._process_root_options(self.parser, rootname)
def _get_parser_items(self, parser, section):
"""Basically implement ConfigParser.items() for pre-Python-2.3 versions."""
@@ -312,67 +203,24 @@ class Config:
for option in parser.options(section):
d[option] = parser.get(section, option)
return d.items()
def get_authorizer_and_params_hack(self, rootname):
"""Return a 2-tuple containing the name and parameters of the
authorizer configured for use with ROOTNAME.
### FIXME: This whole thing is a hack caused by our not being able
### to non-destructively overlay root options when trying to do
### something like a root listing (which might need to get
### different authorizer bits for each and every root in the list).
### Until we have a good way to do that, we expose this function,
### which assumes that base and per-vhost configuration has been
### absorbed into this object and that per-root options have *not*
### been overlayed. See issue #371."""
# We assume that per-root options have *not* been overlayed.
assert(self.root_options_overlayed == 0)
def get_authorizer_params(self, authorizer, rootname=None):
if not self.conf_path:
return None, {}
return {}
# Figure out the authorizer by searching first for a per-root
# override, then falling back to the base/vhost configuration.
authorizer = None
root_options_section = 'root-%s/options' % (rootname)
if self.parser.has_section(root_options_section) \
and self.parser.has_option(root_options_section, 'authorizer'):
authorizer = self.parser.get(root_options_section, 'authorizer')
if not authorizer:
authorizer = self.options.authorizer
# No authorizer? Get outta here.
if not authorizer:
return None, {}
# Dig up the parameters for the authorizer, starting with the
# base/vhost items, then overlaying any root-specific ones we find.
params = {}
authz_section = 'authz-%s' % (authorizer)
if hasattr(self, authz_section):
sub_config = getattr(self, authz_section)
for attr in dir(sub_config):
params[attr] = getattr(sub_config, attr)
root_authz_section = 'root-%s/authz-%s' % (rootname, authorizer)
for section in self.parser.sections():
if section == root_authz_section:
if section == authz_section:
for key, value in self._get_parser_items(self.parser, section):
params[key] = value
return authorizer, params
def get_authorizer_params(self, authorizer=None):
"""Return a dictionary of parameter names and values which belong
to the configured authorizer (or AUTHORIZER, if provided)."""
params = {}
if authorizer is None:
authorizer = self.options.authorizer
if authorizer:
authz_section = 'authz-%s' % (self.options.authorizer)
if hasattr(self, authz_section):
sub_config = getattr(self, authz_section)
for attr in dir(sub_config):
params[attr] = getattr(sub_config, attr)
if rootname:
root_authz_section = 'root-%s/authz-%s' % (rootname, authorizer)
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
params['__config'] = self
return params
def set_defaults(self):
@@ -425,18 +273,21 @@ class Config:
self.options.show_subdir_lastmod = 0
self.options.show_logs = 1
self.options.show_log_in_markup = 1
self.options.cross_copies = 1
self.options.cross_copies = 0
self.options.use_localtime = 0
self.options.short_log_len = 80
self.options.enable_syntax_coloration = 1
self.options.tabsize = 8
self.options.detect_encoding = 0
self.options.use_cvsgraph = 0
self.options.cvsgraph_conf = "cvsgraph.conf"
self.options.allowed_cvsgraph_useropts = []
self.options.use_re_search = 0
self.options.dir_pagesize = 0
self.options.log_pagesize = 0
self.options.limit_changes = 100
self.options.cvs_ondisk_charset = 'cp1251'
self.options.binary_mime_re = '^(?!text/|.*\Wxml)'
self.options.encodings = 'utf-8:cp1251:iso-8859-1'
self.templates.diff = None
self.templates.directory = None
@@ -452,6 +303,7 @@ class Config:
self.cvsdb.enabled = 0
self.cvsdb.host = ''
self.cvsdb.port = 3306
self.cvsdb.socket = ''
self.cvsdb.database_name = ''
self.cvsdb.user = ''
self.cvsdb.passwd = ''
@@ -460,9 +312,8 @@ class Config:
self.cvsdb.row_limit = 1000
self.cvsdb.rss_row_limit = 100
self.cvsdb.check_database_for_root = 0
self.cvsdb.fulltext_min_relevance = 0.2
self.query.viewvc_base_url = None
def _startswith(somestr, substr):
return somestr[:len(substr)] == substr

View File

@@ -37,15 +37,32 @@ error = "cvsdb error"
## defined to actually be complete; it should run well off of any DBI 2.0
## complient database interface
encs = [ "utf-8", "cp1251", "iso-8859-1" ]
def utf8string(value):
for e in encs:
try:
value = value.decode(e)
break
except: pass
return value.encode("utf-8")
def setencs(e):
global encs
encs = e
class CheckinDatabase:
def __init__(self, host, port, user, passwd, database, row_limit):
def __init__(self, host, port, socket, user, passwd, database, row_limit, min_relevance, authorizer = None):
self._host = host
self._port = port
self._socket = socket
self._user = user
self._passwd = passwd
self._database = database
self._row_limit = row_limit
self._version = None
self._min_relevance = min_relevance
self.authorizer = authorizer
## database lookup caches
self._get_cache = {}
@@ -54,7 +71,7 @@ class CheckinDatabase:
def Connect(self):
self.db = dbi.connect(
self._host, self._port, self._user, self._passwd, self._database)
self._host, self._port, self._socket, self._user, self._passwd, self._database)
cursor = self.db.cursor()
cursor.execute("SET AUTOCOMMIT=1")
table_list = self.GetTableList()
@@ -67,14 +84,16 @@ class CheckinDatabase:
else:
self._version = 0
if self._version > CURRENT_SCHEMA_VERSION:
raise DatabaseVersionError("Database version %d is newer than the "
"last version supported by this "
"software." % (self._version))
raise DatabaseVersionError("Database version %d is newer than the "
"last version supported by this "
"software." % (self._version))
def sql_get_id(self, table, column, value, auto_set):
value = utf8string(value)
sql = "SELECT id FROM %s WHERE %s=%%s" % (table, column)
sql_args = (value, )
cursor = self.db.cursor()
cursor.execute(sql, sql_args)
try:
@@ -84,7 +103,7 @@ class CheckinDatabase:
return None
else:
return str(int(id))
## insert the new identifier
sql = "INSERT INTO %s(%s) VALUES(%%s)" % (table, column)
sql_args = (value, )
@@ -235,13 +254,17 @@ class CheckinDatabase:
def GetRepository(self, id):
return self.get("repositories", "repository", id)
def GetRepositoryList(self):
return self.get_list("repositories", repository)
def SQLGetDescriptionID(self, description, auto_set = 1):
description = utf8string(description)
## lame string hash, blame Netscape -JMP
hash = len(description)
sql = "SELECT id FROM descs WHERE hash=%s AND description=%s"
sql_args = (hash, description)
cursor = self.db.cursor()
cursor.execute(sql, sql_args)
try:
@@ -255,7 +278,7 @@ class CheckinDatabase:
sql = "INSERT INTO descs (hash,description) values (%s,%s)"
sql_args = (hash, description)
cursor.execute(sql, sql_args)
return self.GetDescriptionID(description, 0)
def GetDescriptionID(self, description, auto_set = 1):
@@ -291,6 +314,26 @@ class CheckinDatabase:
def GetAuthorList(self):
return self.get_list("people", 1)
def GetLatestCheckinTime(self, repository):
repository_id = self.GetRepositoryID(repository.rootpath, 0)
if repository_id is None:
return None
commits_table = self._version >= 1 and 'commits' or 'checkins'
sql = "SELECT ci_when FROM %s WHERE "\
"repositoryid = %%s ORDER BY ci_when DESC LIMIT 1" % (commits_table)
sql_args = (repository_id)
cursor = self.db.cursor()
cursor.execute(sql, sql_args)
ci_when = None
try:
ci_when = cursor.fetchone()[0]
except TypeError:
return None
return dbi.TicksFromDateTime(ci_when)
def AddCommitList(self, commit_list):
for commit in commit_list:
self.AddCommit(commit)
@@ -358,76 +401,84 @@ class CheckinDatabase:
match = " REGEXP "
elif query_entry.match == "notregex":
match = " NOT REGEXP "
sqlList.append("%s%s%s" % (field, match, self.db.literal(data)))
return "(%s)" % (string.join(sqlList, " OR "))
def CreateSQLQueryString(self, query):
commits_table = self._version >= 1 and 'commits' or 'checkins'
tableList = [(commits_table, None)]
fields = [
commits_table+".*",
"repositories.repository AS repository_name",
"dirs.dir AS dir_name",
"files.file AS file_name"]
tableList = [
(commits_table, None),
("repositories","(%s.repositoryid=repositories.id)" % (commits_table)),
("dirs", "(%s.dirid=dirs.id)" % (commits_table)),
("files", "(%s.fileid=files.id)" % (commits_table))]
condList = []
if len(query.text_query):
tableList.append(("descs", "(descs.id=%s.descid)" % (commits_table)))
temp = "MATCH (descs.description) AGAINST (%s" % (self.db.literal(query.text_query))
condList.append("%s IN BOOLEAN MODE) > %s" % (temp, self._min_relevance))
fields.append("%s) AS relevance" % temp)
else:
fields.append("'' AS relevance")
if len(query.repository_list):
tableList.append(("repositories",
"(%s.repositoryid=repositories.id)"
% (commits_table)))
temp = self.SQLQueryListString("repositories.repository",
query.repository_list)
condList.append(temp)
if len(query.branch_list):
tableList.append(("branches",
"(%s.branchid=branches.id)" % (commits_table)))
tableList.append(("branches", "(%s.branchid=branches.id)" % (commits_table)))
temp = self.SQLQueryListString("branches.branch",
query.branch_list)
condList.append(temp)
if len(query.directory_list):
tableList.append(("dirs",
"(%s.dirid=dirs.id)" % (commits_table)))
temp = self.SQLQueryListString("dirs.dir", query.directory_list)
condList.append(temp)
if len(query.file_list):
tableList.append(("files",
"(%s.fileid=files.id)" % (commits_table)))
tableList.append(("files", "(%s.fileid=files.id)" % (commits_table)))
temp = self.SQLQueryListString("files.file", query.file_list)
condList.append(temp)
if len(query.revision_list):
condList.append("(%s.revision IN (" % (commits_table) + ','.join(map(lambda s: self.db.literal(s), query.revision_list)) + "))")
if len(query.author_list):
tableList.append(("people",
"(%s.whoid=people.id)" % (commits_table)))
tableList.append(("people", "(%s.whoid=people.id)" % (commits_table)))
temp = self.SQLQueryListString("people.who", query.author_list)
condList.append(temp)
if len(query.comment_list):
tableList.append(("descs",
"(%s.descid=descs.id)" % (commits_table)))
tableList.append(("descs", "(%s.descid=descs.id)" % (commits_table)))
temp = self.SQLQueryListString("descs.description",
query.comment_list)
condList.append(temp)
if query.from_date:
temp = "(%s.ci_when>=\"%s\")" \
% (commits_table, str(query.from_date))
temp = "(%s.ci_when>=\"%s\")" % (commits_table, str(query.from_date))
condList.append(temp)
if query.to_date:
temp = "(%s.ci_when<=\"%s\")" \
% (commits_table, str(query.to_date))
temp = "(%s.ci_when<=\"%s\")" % (commits_table, str(query.to_date))
condList.append(temp)
if query.sort == "date":
order_by = "ORDER BY %s.ci_when DESC,descid" % (commits_table)
elif query.sort == "author":
tableList.append(("people",
"(%s.whoid=people.id)" % (commits_table)))
tableList.append(("people", "(%s.whoid=people.id)" % (commits_table)))
order_by = "ORDER BY people.who,descid"
elif query.sort == "file":
tableList.append(("files",
"(%s.fileid=files.id)" % (commits_table)))
tableList.append(("files", "(%s.fileid=files.id)" % (commits_table)))
order_by = "ORDER BY files.file,descid"
elif query.sort == "relevance" and len(query.text_query):
order_by = "ORDER BY relevance DESC,%s.ci_when DESC,descid" % (commits_table)
## exclude duplicates from the table list, and split out join
## conditions from table names. In future, the join conditions
@@ -440,6 +491,7 @@ class CheckinDatabase:
tables.append(table)
if cond is not None: joinConds.append(cond)
fields = string.join(fields, ",")
tables = string.join(tables, ",")
conditions = string.join(joinConds + condList, " AND ")
conditions = conditions and "WHERE %s" % conditions
@@ -452,32 +504,46 @@ class CheckinDatabase:
elif self._row_limit:
limit = "LIMIT %s" % (str(self._row_limit))
sql = "SELECT %s.* FROM %s %s %s %s" \
% (commits_table, tables, conditions, order_by, limit)
sql = "SELECT %s FROM %s %s %s %s" % (
fields, tables, conditions, order_by, limit)
return sql
def check_commit_access(self, repos, dir, file, rev):
if self.authorizer:
rootname = repos.split('/')
rootname = rootname.pop()
path_parts = dir.split('/')
path_parts.append(file)
return self.authorizer.check_path_access(rootname, path_parts, vclib.FILE, rev)
return True
def RunQuery(self, query):
sql = self.CreateSQLQueryString(query)
cursor = self.db.cursor()
cursor.execute(sql)
while 1:
row = cursor.fetchone()
if not row:
break
(dbType, dbCI_When, dbAuthorID, dbRepositoryID, dbDirID,
dbFileID, dbRevision, dbStickyTag, dbBranchID, dbAddedLines,
dbRemovedLines, dbDescID) = row
dbRemovedLines, dbDescID, dbRepositoryName, dbDirName,
dbFileName, dbRelevance) = row
if not self.check_commit_access(dbRepositoryName, dbDirName, dbFileName, dbRevision):
continue
commit = LazyCommit(self)
if dbType == 'Add':
commit.SetTypeAdd()
commit.SetTypeAdd()
elif dbType == 'Remove':
commit.SetTypeRemove()
commit.SetTypeRemove()
else:
commit.SetTypeChange()
commit.SetTypeChange()
commit.SetTime(dbi.TicksFromDateTime(dbCI_When))
commit.SetFileID(dbFileID)
commit.SetDirectoryID(dbDirID)
@@ -488,6 +554,7 @@ class CheckinDatabase:
commit.SetPlusCount(dbAddedLines)
commit.SetMinusCount(dbRemovedLines)
commit.SetDescriptionID(dbDescID)
commit.SetRelevance(dbRelevance)
query.AddCommit(commit)
@@ -534,7 +601,7 @@ class CheckinDatabase:
sql_args = (value, value)
cursor = self.db.cursor()
cursor.execute(sql, sql_args)
def sql_purge(self, table, key, fkey, ftable):
sql = "DELETE FROM %s WHERE %s NOT IN (SELECT %s FROM %s)" \
% (table, key, fkey, ftable)
@@ -613,6 +680,7 @@ class Commit:
self.__pluscount = ''
self.__minuscount = ''
self.__description = ''
self.__relevance = ''
self.__gmt_time = 0.0
self.__type = Commit.CHANGE
@@ -681,6 +749,12 @@ class Commit:
def GetDescription(self):
return self.__description
def SetRelevance(self, relevance):
self.__relevance = relevance
def GetRelevance(self):
return self.__relevance
def SetTypeChange(self):
self.__type = Commit.CHANGE
@@ -781,8 +855,10 @@ class CheckinDatabaseQuery:
self.branch_list = []
self.directory_list = []
self.file_list = []
self.revision_list = []
self.author_list = []
self.comment_list = []
self.text_query = ""
## date range in DBI 2.0 timedate objects
self.from_date = None
@@ -798,6 +874,9 @@ class CheckinDatabaseQuery:
## are added
self.commit_cb = None
def SetTextQuery(self, query):
self.text_query = query
def SetRepository(self, repository, match = "exact"):
self.repository_list.append(QueryEntry(repository, match))
@@ -810,10 +889,15 @@ class CheckinDatabaseQuery:
def SetFile(self, file, match = "exact"):
self.file_list.append(QueryEntry(file, match))
def SetRevision(self, revision):
r = re.compile('\s*[,;]+\s*')
for i in r.split(revision):
self.revision_list.append(i)
def SetAuthor(self, author, match = "exact"):
self.author_list.append(QueryEntry(author, match))
def SetComment(self, comment, match = "exact"):
def SetComment(self, comment, match = "fulltext"):
self.comment_list.append(QueryEntry(comment, match))
def SetSortMethod(self, sort):
@@ -853,20 +937,21 @@ def CreateCommit():
def CreateCheckinQuery():
return CheckinDatabaseQuery()
def ConnectDatabase(cfg, readonly=0):
def ConnectDatabase(cfg, authorizer=None, readonly=0):
if readonly:
user = cfg.cvsdb.readonly_user
passwd = cfg.cvsdb.readonly_passwd
else:
user = cfg.cvsdb.user
passwd = cfg.cvsdb.passwd
db = CheckinDatabase(cfg.cvsdb.host, cfg.cvsdb.port, user, passwd,
cfg.cvsdb.database_name, cfg.cvsdb.row_limit)
db = CheckinDatabase(cfg.cvsdb.host, cfg.cvsdb.port, cfg.cvsdb.socket, user, passwd,
cfg.cvsdb.database_name, cfg.cvsdb.row_limit, cfg.cvsdb.fulltext_min_relevance,
authorizer)
db.Connect()
return db
def ConnectDatabaseReadOnly(cfg):
return ConnectDatabase(cfg, 1)
def ConnectDatabaseReadOnly(cfg, authorizer):
return ConnectDatabase(cfg, authorizer, 1)
def GetCommitListFromRCSFile(repository, path_parts, revision=None):
commit_list = []

View File

@@ -59,5 +59,13 @@ def TicksFromDateTime(datetime):
else:
return time.mktime(t[:8] + (-1,))
def connect(host, port, user, passwd, db):
return MySQLdb.connect(host=host, port=port, user=user, passwd=passwd, db=db)
def connect(host, port, socket, user, passwd, db, charset = 'utf8'):
return MySQLdb.connect(
host = host,
port = port,
unix_socket = socket,
user = user,
passwd = passwd,
db = db,
charset = charset,
use_unicode = charset == 'utf8')

View File

@@ -347,7 +347,7 @@ class Template:
for_names = [ ]
if base_format:
program.append((self._cmd_format, _formatters[base_format]))
program.append((self._cmd_format, _printers[base_format]))
for i in range(len(parts)):
piece = parts[i]
@@ -405,13 +405,13 @@ class Template:
elif cmd == 'format':
if args[1][0]:
# argument is a variable reference
formatter = args[1]
printer = args[1]
else:
# argument is a string constant referring to built-in formatter
formatter = _formatters.get(args[1][1])
if not formatter:
# argument is a string constant referring to built-in printer
printer = _printers.get(args[1][1])
if not printer:
raise UnknownFormatConstantError(str(args[1:]))
program.append((self._cmd_format, formatter))
program.append((self._cmd_format, printer))
# remember the cmd, current pos, args, and a section placeholder
stack.append([cmd, len(program), args[1:], None])
@@ -465,13 +465,13 @@ class Template:
except TypeError:
raise Exception("Unprintable value type for '%s'" % (str(valrefs[0][0])))
def _cmd_format(self, formatter, ctx):
if type(formatter) is TupleType:
formatter = _get_value(formatter, ctx)
ctx.formatters.append(formatter)
def _cmd_format(self, printer, ctx):
if type(printer) is TupleType:
printer = _get_value(printer, ctx)
ctx.printers.append(printer)
def _cmd_end_format(self, valref, ctx):
ctx.formatters.pop()
ctx.printers.pop()
def _cmd_include(self, (valref, reader), ctx):
fname = _get_value(valref, ctx)
@@ -637,23 +637,14 @@ def _get_value((refname, start, rest), ctx):
# string or a sequence
return ob
def _print_formatted(formatters, ctx, chunk):
# print chunk to ctx.fp after running it sequentially through formatters
for formatter in formatters:
chunk = formatter(chunk)
ctx.fp.write(chunk)
def _write_value(value, args, ctx):
# value is a callback function, generates its own output
if callable(value):
apply(value, [ctx] + list(args))
return
# squirrel away formatters in case one of them recursively calls
# _write_value() -- we'll use them (in reverse order) to format our
# output.
formatters = ctx.formatters[:]
formatters.reverse()
# pop printer in case it recursively calls _write_value
printer = ctx.printers.pop()
try:
# if the value has a 'read' attribute, then it is a stream: copy it
@@ -662,7 +653,7 @@ def _write_value(value, args, ctx):
chunk = value.read(16384)
if not chunk:
break
_print_formatted(formatters, ctx, chunk)
printer(ctx, chunk)
# value is a substitution pattern
elif args:
@@ -675,16 +666,14 @@ def _write_value(value, args, ctx):
piece = args[idx]
else:
piece = '<undef>'
_print_formatted(formatters, ctx, piece)
printer(ctx, piece)
# plain old value, write to output
else:
_print_formatted(formatters, ctx, value)
printer(ctx, value)
finally:
# restore our formatters
formatters.reverse()
ctx.formatters = formatters
ctx.printers.append(printer)
class TemplateData:
@@ -726,7 +715,7 @@ class Context:
"""A container for the execution context"""
def __init__(self, fp):
self.fp = fp
self.formatters = []
self.printers = []
def write(self, value, args=()):
_write_value(value, args, self)
@@ -839,26 +828,26 @@ class BaseUnavailableError(EZTException):
class UnknownFormatConstantError(EZTException):
"""The format specifier is an unknown value."""
def _raw_formatter(s):
return s
def _raw_printer(ctx, s):
try: s = s.encode('utf-8')
except: pass
ctx.fp.write(s)
def _html_formatter(s):
return cgi.escape(s)
def _html_printer(ctx, s):
try: s = s.encode('utf-8')
except: pass
ctx.fp.write(cgi.escape(s))
def _xml_formatter(s):
s = s.replace('&', '&#x26;')
s = s.replace('<', '&#x3C;')
s = s.replace('>', '&#x3E;')
return s
def _uri_printer(ctx, s):
try: s = s.encode('utf-8')
except: pass
ctx.fp.write(urllib.quote(s))
def _uri_formatter(s):
return urllib.quote(s)
_formatters = {
FORMAT_RAW : _raw_formatter,
FORMAT_HTML : _html_formatter,
FORMAT_XML : _xml_formatter,
FORMAT_URI : _uri_formatter,
_printers = {
FORMAT_RAW : _raw_printer,
FORMAT_HTML : _html_printer,
FORMAT_XML : _html_printer,
FORMAT_URI : _uri_printer,
}
# --- standard test environment ---
@@ -876,7 +865,7 @@ def test_parse():
['', '["a \\"b[foo]" c.d f]', None, '']
def _test(argv):
import doctest, ezt
import doctest, ezt
verbose = "-v" in argv
return doctest.testmod(ezt, verbose=verbose)

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2010 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC
@@ -20,7 +20,7 @@ import difflib
import sys
import re
import ezt
import sapi
import cgi
def sidebyside(fromlines, tolines, context):
"""Generate side by side diff"""
@@ -49,18 +49,18 @@ def _mdiff_split(flag, (line_number, text)):
while True:
m = _re_mdiff.search(text, pos)
if not m:
segments.append(_item(text=sapi.escape(text[pos:]), type=None))
segments.append(_item(text=cgi.escape(text[pos:]), type=None))
break
if m.start() > pos:
segments.append(_item(text=sapi.escape(text[pos:m.start()]), type=None))
segments.append(_item(text=cgi.escape(text[pos:m.start()]), type=None))
if m.group(1) == "+":
segments.append(_item(text=sapi.escape(m.group(2)), type="add"))
segments.append(_item(text=cgi.escape(m.group(2)), type="add"))
elif m.group(1) == "-":
segments.append(_item(text=sapi.escape(m.group(2)), type="remove"))
segments.append(_item(text=cgi.escape(m.group(2)), type="remove"))
elif m.group(1) == "^":
segments.append(_item(text=sapi.escape(m.group(2)), type="change"))
segments.append(_item(text=cgi.escape(m.group(2)), type="change"))
pos = m.end()
@@ -166,12 +166,12 @@ def _differ_split(row, guide):
for m in _re_differ.finditer(guide, pos):
if m.start() > pos:
segments.append(_item(text=sapi.escape(line[pos:m.start()]), type=None))
segments.append(_item(text=sapi.escape(line[m.start():m.end()]),
segments.append(_item(text=cgi.escape(line[pos:m.start()]), type=None))
segments.append(_item(text=cgi.escape(line[m.start():m.end()]),
type="change"))
pos = m.end()
segments.append(_item(text=sapi.escape(line[pos:]), type=None))
segments.append(_item(text=cgi.escape(line[pos:]), type=None))
return _item(gap=ezt.boolean(gap), type=type, segments=segments,
left_number=left_number, right_number=right_number)

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python
# -*-python-*-
#
# Copyright (C) 1999-2010 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2009 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC
@@ -40,12 +40,22 @@ class FormData:
self.file = ""
self.who = ""
self.sortby = ""
self.textquery = ""
self.date = ""
self.hours = 0
self.decode_thyself(form)
def decode_thyself(self, form):
try:
self.textquery = string.strip(form["textquery"].value)
except KeyError:
pass
except TypeError:
pass
else:
self.valid = 1
try:
self.repository = string.strip(form["repository"].value)
except KeyError:
@@ -245,10 +255,15 @@ def form_to_cvsdb_query(form_data):
cmd = decode_command(cmd)
query.SetAuthor(str, cmd)
if form_data.textquery:
query.SetTextQuery(form_data.textquery)
if form_data.sortby == "author":
query.SetSortMethod("author")
elif form_data.sortby == "file":
query.SetSortMethod("file")
elif form_data.sortby == "relevance" and form_data.textquery:
query.SetSortMethod("relevance")
else:
query.SetSortMethod("date")
@@ -275,30 +290,8 @@ def prev_rev(rev):
return string.join(r, '.')
def is_forbidden(cfg, cvsroot_name, module):
'''Return 1 if MODULE in CVSROOT_NAME is forbidden; return 0 otherwise.'''
# CVSROOT_NAME might be None here if the data comes from an
# unconfigured root. This interfaces doesn't care that the root
# isn't configured, but if that's the case, it will consult only
# the base and per-vhost configuration for authorizer and
# authorizer parameters.
if cvsroot_name:
authorizer, params = cfg.get_authorizer_and_params_hack(cvsroot_name)
else:
authorizer = cfg.options.authorizer
params = cfg.get_authorizer_params()
# If CVSROOT_NAME isn't configured to use an authorizer, nothing
# is forbidden. If it's configured to use something other than
# the 'forbidden' authorizer, complain. Otherwise, check for
# forbiddenness per the PARAMS as expected.
if not authorizer:
return 0
if authorizer != 'forbidden':
raise Exception("The 'forbidden' authorizer is the only one supported "
"by this interface. The '%s' root is configured to "
"use a different one." % (cvsroot_name))
forbidden = params.get('forbidden', '')
auth_params = cfg.get_authorizer_params('forbidden', cvsroot_name)
forbidden = auth_params.get('forbidden', '')
forbidden = map(string.strip, filter(None, string.split(forbidden, ',')))
default = 0
for pat in forbidden:
@@ -311,8 +304,12 @@ def is_forbidden(cfg, cvsroot_name, module):
return default
def build_commit(server, cfg, desc, files, cvsroots, viewvc_link):
ob = _item(num_files=len(files), files=[])
ob.log = desc and string.replace(server.escape(desc), '\n', '<br />') or ''
ob = _item(num_files=len(files), files=[], plus=0, minus=0)
if desc:
ob.log = string.replace(server.escape(desc), '\n', '<br />')
else:
ob.log = '&nbsp;'
for commit in files:
repository = commit.GetRepository()
@@ -346,10 +343,9 @@ def build_commit(server, cfg, desc, files, cvsroots, viewvc_link):
except:
raise Exception, str([directory, commit.GetFile()])
## If we couldn't find the cvsroot path configured in the
## viewvc.conf file, or we don't have a VIEWVC_LINK, then
## don't make the link.
if cvsroot_name and viewvc_link:
## if we couldn't find the cvsroot path configured in the
## viewvc.conf file, then don't make the link
if cvsroot_name:
flink = '[%s] <a href="%s/%s?root=%s">%s</a>' % (
cvsroot_name, viewvc_link, urllib.quote(file),
cvsroot_name, file)
@@ -363,6 +359,9 @@ def build_commit(server, cfg, desc, files, cvsroots, viewvc_link):
flink = '[%s] %s' % (repository, file)
dlink = None
ob.relevance = commit.GetRelevance()
ob.plus += int(commit.GetPlusCount())
ob.minus += int(commit.GetMinusCount())
ob.files.append(_item(date=ctime,
author=commit.GetAuthor(),
link=flink,
@@ -376,9 +375,8 @@ def build_commit(server, cfg, desc, files, cvsroots, viewvc_link):
return ob
def run_query(server, cfg, form_data, viewvc_link):
def run_query(server, cfg, db, form_data, viewvc_link):
query = form_to_cvsdb_query(form_data)
db = cvsdb.ConnectDatabaseReadOnly(cfg)
db.RunQuery(query)
if not query.commit_list:
@@ -388,7 +386,6 @@ def run_query(server, cfg, form_data, viewvc_link):
files = [ ]
cvsroots = {}
viewvc.expand_root_parents(cfg)
rootitems = cfg.general.svn_roots.items() + cfg.general.cvs_roots.items()
for key, value in rootitems:
cvsroots[cvsdb.CleanRepository(value)] = key
@@ -425,33 +422,37 @@ def main(server, cfg, viewvc_link):
form = server.FieldStorage()
form_data = FormData(form)
db = cvsdb.ConnectDatabaseReadOnly(cfg, None)
if form_data.valid:
commits = run_query(server, cfg, form_data, viewvc_link)
commits = run_query(server, cfg, db, form_data, viewvc_link)
query = None
else:
commits = [ ]
query = 'skipped'
docroot = cfg.options.docroot
if docroot is None and viewvc_link:
docroot = viewvc_link + '/' + viewvc.docroot_magic_path
data = ezt.TemplateData({
'cfg' : cfg,
'address' : cfg.general.address,
'vsn' : viewvc.__version__,
'repository' : server.escape(form_data.repository),
'branch' : server.escape(form_data.branch),
'directory' : server.escape(form_data.directory),
'file' : server.escape(form_data.file),
'who' : server.escape(form_data.who),
'docroot' : docroot,
'textquery' : server.escape(form_data.textquery, 1),
'repository' : server.escape(form_data.repository, 1),
'branch' : server.escape(form_data.branch, 1),
'directory' : server.escape(form_data.directory, 1),
'file' : server.escape(form_data.file, 1),
'who' : server.escape(form_data.who, 1),
'docroot' : cfg.options.docroot is None \
and viewvc_link + '/' + viewvc.docroot_magic_path \
or cfg.options.docroot,
'sortby' : form_data.sortby,
'date' : form_data.date,
'query' : query,
'commits' : commits,
'num_commits' : len(commits),
'rss_href' : None,
'repositories' : db.GetRepositoryList(),
'hours' : form_data.hours and form_data.hours or 2,
})

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2010 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC
@@ -20,27 +20,13 @@ import string
import os
import sys
import re
import cgi
# global server object. It will be either a CgiServer, a WsgiServer,
# or a proxy to an AspServer or ModPythonServer object.
# global server object. It will be either a CgiServer or a proxy to
# an AspServer or ModPythonServer object.
server = None
# Simple HTML string escaping. Note that we always escape the
# double-quote character -- ViewVC shouldn't ever need to preserve
# that character as-is, and sometimes needs to embed escaped values
# into HTML attributes.
def escape(s):
s = str(s)
s = string.replace(s, '&', '&amp;')
s = string.replace(s, '>', '&gt;')
s = string.replace(s, '<', '&lt;')
s = string.replace(s, '"', "&quot;")
return s
class Server:
def __init__(self):
self.pageGlobals = {}
@@ -48,9 +34,6 @@ class Server:
def self(self):
return self
def escape(self, s):
return escape(s)
def close(self):
pass
@@ -146,6 +129,9 @@ class CgiServer(Server):
global server
server = self
global cgi
import cgi
def addheader(self, name, value):
self.headers.append((name, value))
@@ -174,6 +160,9 @@ class CgiServer(Server):
self.header(status='301 Moved')
sys.stdout.write('This document is located <a href="%s">here</a>.\n' % url)
def escape(self, s, quote = None):
return cgi.escape(s, quote)
def getenv(self, name, value=None):
ret = os.environ.get(name, value)
if self.iis and name == 'PATH_INFO' and ret:
@@ -186,7 +175,7 @@ class CgiServer(Server):
def FieldStorage(fp=None, headers=None, outerboundary="",
environ=os.environ, keep_blank_values=0, strict_parsing=0):
return cgi.FieldStorage(fp, headers, outerboundary, environ,
keep_blank_values, strict_parsing)
keep_blank_values, strict_parsing)
def write(self, s):
sys.stdout.write(s)
@@ -198,66 +187,6 @@ class CgiServer(Server):
return sys.stdout
class WsgiServer(Server):
def __init__(self, environ, start_response):
Server.__init__(self)
self._environ = environ
self._start_response = start_response;
self._headers = []
self._wsgi_write = None
self.headerSent = False
global server
server = self
global cgi
import cgi
def addheader(self, name, value):
self._headers.append((name, value))
def header(self, content_type='text/html; charset=UTF-8', status=None):
if not status:
status = "200 OK"
if not self.headerSent:
self.headerSent = True
self._headers.insert(0, ("Content-Type", content_type),)
self._wsgi_write = self._start_response("%s" % status, self._headers)
def redirect(self, url):
"""Redirect client to url. This discards any data that has been queued
to be sent to the user. But there should never by any anyway.
"""
self.addheader('Location', url)
self.header(status='301 Moved')
self._wsgi_write('This document is located <a href="%s">here</a>.' % url)
def escape(self, s, quote = None):
return cgi.escape(s, quote)
def getenv(self, name, value=None):
return self._environ.get(name, value)
def params(self):
return cgi.parse(environ=self._environ, fp=self._environ["wsgi.input"])
def FieldStorage(self, fp=None, headers=None, outerboundary="",
environ=os.environ, keep_blank_values=0, strict_parsing=0):
return cgi.FieldStorage(self._environ["wsgi.input"], headers,
outerboundary, self._environ, keep_blank_values,
strict_parsing)
def write(self, s):
self._wsgi_write(s)
def flush(self):
pass
def file(self):
return File(self)
class AspServer(ThreadedServer):
def __init__(self, Server, Request, Response, Application):
ThreadedServer.__init__(self)
@@ -290,6 +219,9 @@ class AspServer(ThreadedServer):
def redirect(self, url):
self.response.Redirect(url)
def escape(self, s, quote = None):
return self.server.HTMLEncode(str(s))
def getenv(self, name, value = None):
ret = self.request.ServerVariables(name)()
if not type(ret) is types.UnicodeType:
@@ -351,6 +283,9 @@ class ModPythonServer(ThreadedServer):
self.request = request
self.headerSent = 0
global cgi
import cgi
def addheader(self, name, value):
self.request.headers_out.add(name, value)
@@ -373,6 +308,9 @@ class ModPythonServer(ThreadedServer):
self.request.write("You are being redirected to <a href=\"%s\">%s</a>"
% (url, url))
def escape(self, s, quote = None):
return cgi.escape(s, quote)
def getenv(self, name, value = None):
try:
return self.request.subprocess_env[name]

View File

@@ -0,0 +1,100 @@
# -*-python-*-
#
# Copyright (C) 2009 Vitaliy Filippov.
#
# 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
# distribution or at http://viewvc.org/license-1.html.
#
# For more information, visit http://viewvc.org/
#
# -----------------------------------------------------------------------
import vcauth
import vclib
import string
from xml.dom.ext.reader import Sax2
from xml import xpath
class ViewVCAuthorizer(vcauth.GenericViewVCAuthorizer):
"""An authorizer making use of CVSnt access control lists (which are in form
of XML files in CVS/ subdirectories in the repository)."""
def __init__(self, username, params={}):
self.username = username
self.params = params
self.cfg = params['__config']
self.default = params.get('default', 0)
self.cached = {}
self.xmlcache = {}
def checkr(self, element, paths):
r = None
for p in paths:
nodes = xpath.Evaluate(p, element)
if nodes and len(nodes):
for c in nodes:
if c.nodeName == 'read' and r is None:
r = True
if c.attributes and len(c.attributes):
for a in c.attributes:
if a.nodeName == 'deny':
r = not a.value
if r is not None:
break
return r
def check(self, rootname, path_parts, filename):
d = self.cfg.general.cvs_roots.get(rootname,None)
if not d:
return self.default
i = len(path_parts)
r = None
while i >= 0:
try:
xml = d
if len(path_parts):
xml = xml + '/' + string.join(path_parts, '/')
xml = xml + '/CVS/fileattr.xml'
if self.cached.get(xml, None) is not None:
return self.cached.get(xml, None)
doc = self.xmlcache.get(xml, None)
if doc is None:
fp = open(xml, 'rb')
doc = Sax2.Reader().fromStream(fp)
fp.close()
self.xmlcache[xml] = doc
if filename:
r = self.checkr(doc.documentElement, [
'/fileattr/file[@name=\'%s\']/acl[@user=\'%s\' and not(@branch)]/read' % (filename, self.username),
'/fileattr/file[@name=\'%s\']/acl[not(@user) and not(@branch)]/read' % filename,
'/fileattr/directory/acl[@user=\'%s\' and not(@branch)]/read' % self.username,
'/fileattr/directory/acl[not(@user) and not(@branch)]/read'
] )
else:
r = self.checkr(doc.documentElement, [
'/fileattr/directory/acl[@user=\'%s\' and not(@branch)]/read' % self.username,
'/fileattr/directory/acl[not(@user) and not(@branch)]/read'
] )
if r is not None:
self.cached[xml] = r
return r
raise Exception(None)
except:
if len(path_parts) > 0:
path_parts = path_parts[:-1]
filename = ''
i = i-1
return self.default
def check_root_access(self, rootname):
return self.check(rootname, [], '')
def check_path_access(self, rootname, path_parts, pathtype, rev=None):
if not path_parts:
return 1
if pathtype == vclib.DIR:
return self.check(rootname, path_parts, '')
f = path_parts[-1]
path_parts = path_parts[:-1]
return self.check(rootname, path_parts, f)

View File

@@ -0,0 +1,57 @@
# -*-python-*-
#
# Copyright (C) 2009 Vitaliy Filippov.
#
# 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
# distribution or at http://viewvc.org/license-1.html.
#
# For more information, visit http://viewvc.org/
#
# -----------------------------------------------------------------------
import vcauth
import vclib
import string
import grp
import re
class ViewVCAuthorizer(vcauth.GenericViewVCAuthorizer):
"""A simple authorizer checking system group membership for every
repository and every user."""
def __init__(self, username, params={}):
self.username = username
self.params = params
self.cfg = params['__config']
self.cached = {}
self.grp = {}
self.byroot = {}
self.fmt = map(lambda l: l.strip(), params.get('group_name_format', 'svn.%s|svn.%s.ro').split('|'))
byr = params.get('by_root', '')
for i in byr.split(','):
if i.find(':') < 0:
continue
(root, auth) = i.split(':', 2)
self.byroot[root.strip()] = map(lambda l: l.strip(), auth.split('|'))
def check_root_access(self, rootname):
r = self.cached.get(rootname, None)
if r is not None:
return r
try:
grent = self.grp.get(rootname, None)
if grent is None:
grent = map(lambda grn: grp.getgrnam(grn.replace('%s', re.sub('[^\w\.\-]+', '', rootname))), self.byroot.get(rootname, self.fmt))
self.grp[rootname] = grent
for i in grent:
if i.gr_mem and len(i.gr_mem) and self.username in i.gr_mem:
r = 1
break
except:
r = 0
self.cached[rootname] = r
return r
def check_path_access(self, rootname, path_parts, pathtype, rev=None):
return self.check_root_access(rootname)

View File

@@ -0,0 +1,69 @@
# -*-python-*-
#
# Copyright (C) 2009 Vitaliy Filippov.
#
# 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
# distribution or at http://viewvc.org/license-1.html.
#
# For more information, visit http://viewvc.org/
#
# -----------------------------------------------------------------------
import vcauth
import vclib
import string
class ViewVCAuthorizer(vcauth.GenericViewVCAuthorizer):
"""A 'union' authorizer: it makes possible to use one authorizer for
one root and other authorizer for other roots."""
def __init__(self, username, params={}):
self.username = username
self.params = params
self.cfg = params['__config']
self.default = params.get('default', '')
self.byroot = {}
self.authz = {}
union = params.get('union', '')
for i in union.split(','):
if i.find(':') < 0:
continue
(root, auth) = i.split(':', 2)
self.byroot[root.strip()] = auth.strip()
def create_authz(self, rootname):
aname = self.byroot.get(rootname, '') or self.default
if not aname:
return None
if self.authz.get(aname, None):
return self.authz[aname]
import imp
fp = None
try:
try:
fp, path, desc = imp.find_module(aname, vcauth.__path__)
my_auth = imp.load_module('viewvc', fp, path, desc)
except ImportError:
raise debug.ViewVCException(
'Invalid authorizer (%s) specified for root "%s"' \
% (self.cfg.options.authorizer, rootname),
'500 Internal Server Error')
finally:
if fp:
fp.close()
params = self.cfg.get_authorizer_params(aname, rootname)
self.authz[aname] = my_auth.ViewVCAuthorizer(self.username, params)
return self.authz[aname]
def check_root_access(self, rootname):
a = self.create_authz(rootname)
if a:
return a.check_root_access(rootname)
return None
def check_path_access(self, rootname, path_parts, pathtype, rev=None):
a = self.create_authz(rootname)
if a:
return a.check_path_access(rootname, path_parts, pathtype, rev)
return None

View File

@@ -180,10 +180,8 @@ class Repository:
rev is the revision of the item to return information about
Return value is a 5-tuple containing: the date, author, log
message, a list of ChangedPath items representing paths changed,
and a dictionary mapping property names to property values for
properties stored on an item.
Return value is a 4-tuple containing the date, author, log
message, and a list of ChangedPath items representing paths changed
Raise vclib.UnsupportedFeature if the version control system
doesn't support a global revision concept.
@@ -330,7 +328,7 @@ def _diff_args(type, options):
if type == CONTEXT:
if options.has_key('context'):
if options['context'] is None:
args.append('--context=-1')
args.append('--context=%i' % 0x01ffffff)
else:
args.append('--context=%i' % options['context'])
else:
@@ -338,7 +336,7 @@ def _diff_args(type, options):
elif type == UNIFIED:
if options.has_key('context'):
if options['context'] is None:
args.append('--unified=-1')
args.append('--unified=%i' % 0x01ffffff)
else:
args.append('--unified=%i' % options['context'])
else:
@@ -383,13 +381,13 @@ class _diff_fp:
self.fp = None
finally:
try:
if self.temp1:
if self.temp1 and self.temp1 != '/dev/null':
os.remove(self.temp1)
self.temp1 = None
self.temp1 = None
finally:
if self.temp2:
if self.temp2 and self.temp2 != '/dev/null':
os.remove(self.temp2)
self.temp2 = None
self.temp2 = None
def __del__(self):
self.close()

View File

@@ -21,6 +21,7 @@ import stat
import string
import re
import time
import cvsdb
# ViewVC libs
import compat
@@ -322,7 +323,7 @@ class BinCVSRepository(BaseCVSRepository):
if self.itemtype(path_parts, rev) != vclib.FILE: # does auth-check
raise vclib.Error("Path '%s' is not a file."
% (string.join(path_parts, "/")))
from vclib.ccvs import blame
source = blame.BlameSource(self.rcsfile(path_parts, 1), rev)
return source, source.revision
@@ -337,13 +338,19 @@ class BinCVSRepository(BaseCVSRepository):
ignore_keyword_subst - boolean, ignore keyword substitution
"""
if not path_parts1:
path_parts1 = path_parts2
rev1 = '1.0'
if not path_parts2:
path_parts2 = path_parts1
rev2 = '1.0'
if self.itemtype(path_parts1, rev1) != vclib.FILE: # does auth-check
raise vclib.Error("Path '%s' is not a file."
% (string.join(path_parts1, "/")))
if self.itemtype(path_parts2, rev2) != vclib.FILE: # does auth-check
raise vclib.Error("Path '%s' is not a file."
% (string.join(path_parts2, "/")))
args = vclib._diff_args(type, options)
if options.get('ignore_keyword_subst', 0):
args.append('-kk')
@@ -351,8 +358,7 @@ class BinCVSRepository(BaseCVSRepository):
rcsfile = self.rcsfile(path_parts1, 1)
if path_parts1 != path_parts2:
raise NotImplementedError, "cannot diff across paths in cvs"
args.extend(['-r' + rev1, '-r' + rev2, rcsfile])
args.extend(['-N', '-r' + rev1, '-r' + rev2, rcsfile])
fp = self.rcs_popen('rcsdiff', args, 'rt')
# Eat up the non-GNU-diff-y headers.
@@ -361,7 +367,6 @@ class BinCVSRepository(BaseCVSRepository):
if not line or line[0:5] == 'diff ':
break
return fp
class CVSDirEntry(vclib.DirEntry):
def __init__(self, name, kind, errors, in_attic, absent=0):
@@ -833,6 +838,8 @@ def _parse_log_entry(fp):
raise ValueError, 'invalid year'
date = compat.timegm(tm)
log = cvsdb.utf8string(log)
return Revision(rev, date,
# author, state, lines changed
match.group(2), match.group(3) == "dead", match.group(5),

View File

@@ -32,6 +32,7 @@ import time
import math
import rcsparse
import vclib
import cvsdb
class CVSParser(rcsparse.Sink):
# Precompiled regular expressions
@@ -446,7 +447,7 @@ class BlameSource:
prev_rev = self.parser.prev_revision.get(rev)
line_number = idx + 1
author = self.parser.revision_author[rev]
thisline = self.lines[idx]
thisline = cvsdb.utf8string(self.lines[idx])
### TODO: Put a real date in here.
item = vclib.Annotation(thisline, line_number, rev, prev_rev, author, None)
self.last = item

View File

@@ -19,6 +19,7 @@ import tempfile
import vclib
import rcsparse
import blame
import cvsdb
### The functionality shared with bincvs should probably be moved to a
### separate module
@@ -66,6 +67,7 @@ class CCVSRepository(BaseCVSRepository):
entry.path = path
try:
rcsparse.parse(open(path, 'rb'), InfoSink(entry, rev, alltags))
entry.log = cvsdb.utf8string(entry.log)
except IOError, e:
entry.errors.append("rcsparse error: %s" % e)
except RuntimeError, e:
@@ -119,23 +121,32 @@ class CCVSRepository(BaseCVSRepository):
return filtered_revs
def rawdiff(self, path_parts1, rev1, path_parts2, rev2, type, options={}):
if self.itemtype(path_parts1, rev1) != vclib.FILE: # does auth-check
if path_parts1 and self.itemtype(path_parts1, rev1) != vclib.FILE: # does auth-check
raise vclib.Error("Path '%s' is not a file."
% (string.join(path_parts1, "/")))
if self.itemtype(path_parts2, rev2) != vclib.FILE: # does auth-check
if path_parts2 and self.itemtype(path_parts2, rev2) != vclib.FILE: # does auth-check
raise vclib.Error("Path '%s' is not a file."
% (string.join(path_parts2, "/")))
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())
if not path_parts1 and not path_parts2:
raise vclib.Error("Nothing to diff.")
r1 = self.itemlog(path_parts1, rev1, vclib.SORTBY_DEFAULT, 0, 0, {})[-1]
r2 = self.itemlog(path_parts2, rev2, vclib.SORTBY_DEFAULT, 0, 0, {})[-1]
if path_parts1:
temp1 = tempfile.mktemp()
open(temp1, 'wb').write(self.openfile(path_parts1, rev1)[0].getvalue())
r1 = self.itemlog(path_parts1, rev1, vclib.SORTBY_DEFAULT, 0, 0, {})[-1]
info1 = (self.rcsfile(path_parts1, root=1, v=0), r1.date, r1.string)
else:
temp1 = '/dev/null'
info1 = ('/dev/null', '', '')
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)
if path_parts2:
temp2 = tempfile.mktemp()
open(temp2, 'wb').write(self.openfile(path_parts2, rev2)[0].getvalue())
r2 = self.itemlog(path_parts2, rev2, vclib.SORTBY_DEFAULT, 0, 0, {})[-1]
info2 = (self.rcsfile(path_parts2, root=1, v=0), r2.date, r2.string)
else:
temp2 = '/dev/null'
info2 = ('/dev/null', '', '')
diff_args = vclib._diff_args(type, options)

View File

@@ -19,11 +19,7 @@ import string
import common
class _TokenStream:
token_term = string.whitespace + ";:"
try:
token_term = frozenset(token_term)
except NameError:
pass
token_term = string.whitespace + ';:'
# the algorithm is about the same speed for any CHUNK_SIZE chosen.
# grab a good-sized chunk, but not too large to overwhelm memory.
@@ -48,17 +44,15 @@ class _TokenStream:
# out more complex solutions.
buf = self.buf
lbuf = len(buf)
idx = self.idx
while 1:
if idx == lbuf:
if idx == len(buf):
buf = self.rcsfile.read(self.CHUNK_SIZE)
if buf == '':
# signal EOF by returning None as the token
del self.buf # so we fail if get() is called again
return None
lbuf = len(buf)
idx = 0
if buf[idx] not in string.whitespace:
@@ -66,7 +60,7 @@ class _TokenStream:
idx = idx + 1
if buf[idx] in ';:':
if buf[idx] == ';' or buf[idx] == ':':
self.buf = buf
self.idx = idx + 1
return buf[idx]
@@ -76,18 +70,17 @@ class _TokenStream:
token = ''
while 1:
# find token characters in the current buffer
while end < lbuf and buf[end] not in self.token_term:
while end < len(buf) and buf[end] not in self.token_term:
end = end + 1
token = token + buf[idx:end]
if end < lbuf:
if end < len(buf):
# we stopped before the end, so we have a full token
idx = end
break
# we stopped at the end of the buffer, so we may have a partial token
buf = self.rcsfile.read(self.CHUNK_SIZE)
lbuf = len(buf)
idx = end = 0
self.buf = buf
@@ -101,24 +94,22 @@ class _TokenStream:
chunks = [ ]
while 1:
if idx == lbuf:
if idx == len(buf):
idx = 0
buf = self.rcsfile.read(self.CHUNK_SIZE)
if buf == '':
raise RuntimeError, 'EOF'
lbuf = len(buf)
i = string.find(buf, '@', idx)
if i == -1:
chunks.append(buf[idx:])
idx = lbuf
idx = len(buf)
continue
if i == lbuf - 1:
if i == len(buf) - 1:
chunks.append(buf[idx:i])
idx = 0
buf = '@' + self.rcsfile.read(self.CHUNK_SIZE)
if buf == '@':
raise RuntimeError, 'EOF'
lbuf = len(buf)
continue
if buf[i + 1] == '@':
chunks.append(buf[idx:i+1])

View File

@@ -18,9 +18,10 @@ import os
import string
import re
import tempfile
import popen2
import time
import urllib
from svn_repos import Revision, SVNChangedPath, _datestr_to_date, _compare_paths, _path_parts, _cleanup_path, _rev2optrev, _fix_subversion_exception, _split_revprops
from svn_repos import Revision, SVNChangedPath, _datestr_to_date, _compare_paths, _path_parts, _cleanup_path, _rev2optrev, _fix_subversion_exception
from svn import core, delta, client, wc, ra
@@ -51,29 +52,6 @@ def get_directory_props(ra_session, path, rev):
props = ra.svn_ra_get_dir(ra_session, path, rev)
return props
def client_log(url, start_rev, end_rev, log_limit, cross_copies,
cb_func, ctx):
try:
client.svn_client_log4([url], start_rev, start_rev, end_rev,
log_limit, 1, not cross_copies, 0, None,
cb_func, ctx)
except NameError:
# Wrap old svn_log_message_receiver_t interface with a
# svn_log_entry_t one.
def cb_convert(paths, revision, author, date, message, pool):
class svn_log_entry_t:
pass
log_entry = svn_log_entry_t()
log_entry.changed_paths = paths
log_entry.revision = revision
log_entry.revprops = { core.SVN_PROP_REVISION_LOG : message,
core.SVN_PROP_REVISION_AUTHOR : author,
core.SVN_PROP_REVISION_DATE : date,
}
cb_func(log_entry, pool)
client.svn_client_log2([url], start_rev, end_rev, log_limit,
1, not cross_copies, cb_convert, ctx)
### END COMPATABILITY CODE ###
@@ -90,11 +68,7 @@ class LogCollector:
self.show_all_logs = show_all_logs
self.lockinfo = lockinfo
def add_log(self, log_entry, pool):
paths = log_entry.changed_paths
revision = log_entry.revision
msg, author, date, revprops = _split_revprops(log_entry.revprops)
def add_log(self, paths, revision, author, date, message, pool):
# Changed paths have leading slashes
changed_paths = paths.keys()
changed_paths.sort(lambda a, b: _compare_paths(a, b))
@@ -114,8 +88,8 @@ class LogCollector:
if change.copyfrom_path:
this_path = change.copyfrom_path + self.path[len(changed_path):]
if self.show_all_logs or this_path:
entry = Revision(revision, date, author, msg, None, self.lockinfo,
self.path[1:], None, None)
entry = Revision(revision, _datestr_to_date(date), author, message, None,
self.lockinfo, self.path[1:], None, None)
self.logs.append(entry)
if this_path:
self.path = this_path
@@ -183,7 +157,7 @@ class RemoteSubversionRepository(vclib.Repository):
# Setup the client context baton, complete with non-prompting authstuffs.
# TODO: svn_cmdline_setup_auth_baton() is mo' better (when available)
core.svn_config_ensure(self.config_dir)
self.ctx = client.svn_client_create_context()
self.ctx = client.svn_client_ctx_t()
self.ctx.auth_baton = core.svn_auth_open([
client.svn_client_get_simple_provider(),
client.svn_client_get_username_provider(),
@@ -278,7 +252,7 @@ class RemoteSubversionRepository(vclib.Repository):
if not vclib.check_path_access(self, entry_path_parts, entry.kind, rev):
continue
dirent = dirents[entry.name]
entry.date, entry.author, entry.log, revprops, changes = \
entry.date, entry.author, entry.log, changes = \
self.revinfo(dirent.created_rev)
entry.rev = str(dirent.created_rev)
entry.size = dirent.size
@@ -309,8 +283,9 @@ class RemoteSubversionRepository(vclib.Repository):
log_limit = 0
if limit:
log_limit = first + limit
client_log(url, _rev2optrev(rev), _rev2optrev(1), log_limit,
cross_copies, lc.add_log, self.ctx)
client.svn_client_log2([url], _rev2optrev(rev), _rev2optrev(1),
log_limit, 1, not cross_copies,
lc.add_log, self.ctx)
revs = lc.logs
revs.sort()
prev = None
@@ -362,36 +337,51 @@ class RemoteSubversionRepository(vclib.Repository):
if not cached_info:
cached_info = self._revinfo_raw(rev)
self._revinfo_cache[rev] = cached_info
return tuple(cached_info)
return cached_info[0], cached_info[1], cached_info[2], cached_info[3]
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)
if not vclib.check_path_access(self, path_parts1, vclib.FILE, rev1):
raise vclib.ItemNotFound(path_parts1)
if not vclib.check_path_access(self, path_parts2, vclib.FILE, rev2):
raise vclib.ItemNotFound(path_parts2)
if path_parts1 is not None:
p1 = self._getpath(path_parts1)
r1 = self._getrev(rev1)
if not vclib.check_path_access(self, path_parts1, vclib.FILE, rev1):
raise vclib.ItemNotFound(path_parts1)
else:
p1 = None
if path_parts2 is not None:
if not p1:
raise vclib.ItemNotFound(parh_parts2)
p2 = self._getpath(path_parts2)
r2 = self._getrev(rev2)
if not vclib.check_path_access(self, path_parts2, vclib.FILE, rev2):
raise vclib.ItemNotFound(path_parts2)
else:
p2 = None
args = vclib._diff_args(type, options)
def _date_from_rev(rev):
date, author, msg, revprops, changes = self.revinfo(rev)
date, author, msg, changes = self.revinfo(rev)
return date
try:
temp1 = temp_checkout(self, p1, r1)
temp2 = temp_checkout(self, p2, r2)
info1 = p1, _date_from_rev(r1), r1
info2 = p2, _date_from_rev(r2), r2
if p1:
temp1 = temp_checkout(self, p1, r1)
else:
temp1 = '/dev/null'
if p2:
temp2 = temp_checkout(self, p2, r2)
else:
temp2 = '/dev/null'
return vclib._diff_fp(temp1, temp2, info1, info2, self.diff_cmd, args)
except core.SubversionException, e:
_fix_subversion_exception(e)
if e.apr_err == vclib.svn.core.SVN_ERR_FS_NOT_FOUND:
if e.apr_err == core.SVN_ERR_FS_NOT_FOUND:
raise vclib.InvalidRevision
raise
def isexecutable(self, path_parts, rev):
props = self.itemprops(path_parts, rev) # does authz-check
return props.has_key(core.SVN_PROP_EXECUTABLE)
@@ -442,54 +432,29 @@ class RemoteSubversionRepository(vclib.Repository):
return revisions[0]
def _revinfo_raw(self, rev):
# return 5-tuple (date, author, msg, revprops, changes)
# return 5-tuple (date, author, message, changes)
optrev = _rev2optrev(rev)
revs = []
def _log_cb(log_entry, pool, retval=revs):
### Subversion 1.5 and earlier didn't offer the 'changed_paths2'
### hash, and in Subversion 1.6, it's offered but broken.
try:
changed_paths = log_entry.changed_paths2
paths = (changed_paths or {}).keys()
except:
changed_paths = log_entry.changed_paths
paths = (changed_paths or {}).keys()
paths.sort(lambda a, b: _compare_paths(a, b))
revision = log_entry.revision
msg, author, date, revprops = _split_revprops(log_entry.revprops)
def _log_cb(changed_paths, revision, author,
datestr, message, pool, retval=revs):
date = _datestr_to_date(datestr)
action_map = { 'D' : vclib.DELETED,
'A' : vclib.ADDED,
'R' : vclib.REPLACED,
'M' : vclib.MODIFIED,
}
paths = (changed_paths or {}).keys()
paths.sort(lambda a, b: _compare_paths(a, b))
changes = []
found_readable = found_unreadable = 0
for path in paths:
change = changed_paths[path]
### svn_log_changed_path_t (which we might get instead of the
### svn_log_changed_path2_t we'd prefer) doesn't have the
### 'node_kind' member.
pathtype = None
if hasattr(change, 'node_kind'):
if change.node_kind == core.svn_node_dir:
pathtype = vclib.DIR
elif change.node_kind == core.svn_node_file:
pathtype = vclib.FILE
### svn_log_changed_path2_t only has the 'text_modified' and
### 'props_modified' bits in Subversion 1.7 and beyond. And
### svn_log_changed_path_t is without.
text_modified = props_modified = 0
if hasattr(change, 'text_modified'):
if change.text_modified == core.svn_tristate_true:
text_modified = 1
if hasattr(change, 'props_modified'):
if change.props_modified == core.svn_tristate_true:
props_modified = 1
change = changed_paths[path]
action = action_map.get(change.action, vclib.MODIFIED)
### Wrong, diddily wrong wrong wrong. Can you say,
### "Manufacturing data left and right because it hurts to
### figure out the right stuff?"
action = action_map.get(change.action, vclib.MODIFIED)
if change.copyfrom_path and change.copyfrom_rev:
is_copy = 1
base_path = change.copyfrom_path
@@ -512,21 +477,21 @@ class RemoteSubversionRepository(vclib.Repository):
base_path = None
base_rev = None
changes.append(SVNChangedPath(path, revision, pathtype, base_path,
base_rev, action, is_copy,
text_modified, props_modified))
base_rev, action, is_copy, 0, 0))
found_readable = 1
else:
found_unreadable = 1
if found_unreadable:
msg = None
message = None
if not found_readable:
author = None
date = None
revs.append([date, author, msg, revprops, changes])
revs.append([date, author, message, changes])
client_log(self.rootpath, optrev, optrev, 1, 0, _log_cb, self.ctx)
return tuple(revs[0])
client.svn_client_log([self.rootpath], optrev, optrev,
1, 0, _log_cb, self.ctx)
return revs[0][0], revs[0][1], revs[0][2], revs[0][3]
##--- custom --##

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2010 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2009 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC
@@ -15,9 +15,11 @@
import vclib
import os
import os.path
import stat
import string
import cStringIO
import signal
import shutil
import time
import tempfile
import popen
@@ -29,13 +31,6 @@ from svn import fs, repos, core, client, delta
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)"
### Pre-1.5 Subversion doesn't have SVN_ERR_CEASE_INVOCATION
try:
_SVN_ERR_CEASE_INVOCATION = core.SVN_ERR_CEASE_INVOCATION
except:
_SVN_ERR_CEASE_INVOCATION = core.SVN_ERR_CANCELLED
### Pre-1.5 SubversionException's might not have the .msg and .apr_err members
def _fix_subversion_exception(e):
if not hasattr(e, 'apr_err'):
@@ -115,27 +110,6 @@ def _rootpath2url(rootpath, path):
return 'file://' + string.join([rootpath, path], "/")
# Given a dictionary REVPROPS of revision properties, pull special
# ones out of them and return a 4-tuple containing the log message,
# the author, the date (converted from the date string property), and
# a dictionary of any/all other revprops.
def _split_revprops(revprops):
if not revprops:
return None, None, None, {}
special_props = []
for prop in core.SVN_PROP_REVISION_LOG, \
core.SVN_PROP_REVISION_AUTHOR, \
core.SVN_PROP_REVISION_DATE:
if revprops.has_key(prop):
special_props.append(revprops[prop])
del(revprops[prop])
else:
special_props.append(None)
msg, author, datestr = tuple(special_props)
date = _datestr_to_date(datestr)
return msg, author, date, revprops
def _datestr_to_date(datestr):
try:
return core.svn_time_from_cstring(datestr) / 1000000
@@ -199,7 +173,7 @@ class NodeHistory:
return
self.histories.append([revision, _cleanup_path(path)])
if self.limit and len(self.histories) == self.limit:
raise core.SubversionException("", _SVN_ERR_CEASE_INVOCATION)
raise core.SubversionException("", core.SVN_ERR_CEASE_INVOCATION)
def __getitem__(self, idx):
return self.histories[idx]
@@ -226,7 +200,7 @@ def _get_history(svnrepos, path, rev, path_type, limit=0, options={}):
1, rev, options.get('svn_cross_copies', 0))
except core.SubversionException, e:
_fix_subversion_exception(e)
if e.apr_err != _SVN_ERR_CEASE_INVOCATION:
if e.apr_err != core.SVN_ERR_CEASE_INVOCATION:
raise
# Now, iterate over those history items, checking for changes of
@@ -246,7 +220,7 @@ def _log_helper(svnrepos, path, rev, lockinfo):
copyfrom_rev, copyfrom_path = fs.copied_from(rev_root, path)
# Assemble our LogEntry
date, author, msg, revprops, changes = svnrepos._revinfo(rev)
date, author, msg, changes = svnrepos.revinfo(rev)
if fs.is_file(rev_root, path):
size = fs.file_length(rev_root, path)
else:
@@ -335,14 +309,14 @@ class FileContentsPipe:
class BlameSource:
def __init__(self, local_url, rev, first_rev, config_dir):
def __init__(self, local_url, rev, first_rev):
self.idx = -1
self.first_rev = first_rev
self.blame_data = []
ctx = client.svn_client_create_context()
core.svn_config_ensure(config_dir)
ctx.config = core.svn_config_get_config(config_dir)
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:
### TODO: Is this use of FIRST_REV always what we want? Should we
@@ -397,7 +371,7 @@ class LocalSubversionRepository(vclib.Repository):
self.auth = authorizer
self.svn_client_path = utilities.svn or 'svn'
self.diff_cmd = utilities.diff or 'diff'
self.config_dir = config_dir or None
self.config_dir = config_dir
# See if this repository is even viewable, authz-wise.
if not vclib.check_root_access(self):
@@ -493,7 +467,7 @@ class LocalSubversionRepository(vclib.Repository):
continue
path = self._getpath(entry_path_parts)
entry_rev = _get_last_history_rev(fsroot, path)
date, author, msg, revprops, changes = self._revinfo(entry_rev)
date, author, msg, changes = self.revinfo(entry_rev)
entry.rev = str(entry_rev)
entry.date = date
entry.author = author
@@ -588,146 +562,141 @@ class LocalSubversionRepository(vclib.Repository):
youngest_rev, youngest_path = history[0]
oldest_rev, oldest_path = history[-1]
source = BlameSource(_rootpath2url(self.rootpath, path),
youngest_rev, oldest_rev, self.config_dir)
youngest_rev, oldest_rev)
return source, youngest_rev
def _revinfo(self, rev, include_changed_paths=0):
"""Internal-use, cache-friendly revision information harvester."""
def _revinfo_helper(rev, include_changed_paths):
# Get the revision property info. (Would use
# editor.get_root_props(), but something is broken there...)
revprops = fs.revision_proplist(self.fs_ptr, rev)
msg, author, date, revprops = _split_revprops(revprops)
# Optimization: If our caller doesn't care about the changed
# paths, and we don't need them to do authz determinations, let's
# get outta here.
if self.auth is None and not include_changed_paths:
return date, author, msg, revprops, None
# If we get here, then we either need the changed paths because we
# were asked for them, or we need them to do authorization checks.
# Either way, we need 'em, so let's get 'em.
fsroot = self._getroot(rev)
editor = repos.ChangeCollector(self.fs_ptr, fsroot)
e_ptr, e_baton = delta.make_editor(editor)
repos.svn_repos_replay(fsroot, e_ptr, e_baton)
changedpaths = {}
changes = editor.get_changes()
# Copy the Subversion changes into a new hash, checking
# authorization and converting them into ChangedPath objects.
found_readable = found_unreadable = 0
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
parts = _path_parts(path)
if vclib.check_path_access(self, parts, pathtype, rev):
if is_copy and change.base_path and (change.base_path != path):
parts = _path_parts(change.base_path)
if not vclib.check_path_access(self, parts, pathtype, change.base_rev):
is_copy = 0
change.base_path = None
change.base_rev = None
changedpaths[path] = SVNChangedPath(path, rev, pathtype,
change.base_path,
change.base_rev, action,
is_copy, change.text_changed,
change.prop_changes)
found_readable = 1
else:
found_unreadable = 1
# If our caller doesn't care about changed paths, we must be
# here for authz reasons only. That means the minute we've
# found both a readable and an unreadable path, we can bail out.
if (not include_changed_paths) and found_readable and found_unreadable:
return date, author, None, None, None
# Okay, we've process all our paths. Let's filter our metadata,
# and return the requested data.
if found_unreadable:
msg = None
if not found_readable:
author = None
date = None
if include_changed_paths:
return date, author, msg, revprops, changedpaths.values()
else:
return date, author, msg, revprops, None
def _revinfo_raw(self, rev):
fsroot = self._getroot(rev)
# Consult the revinfo cache first. If we don't have cached info,
# or our caller wants changed paths and we don't have those for
# this revision, go do the real work.
# 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...
revprops = fs.revision_proplist(self.fs_ptr, rev)
msg = revprops.get(core.SVN_PROP_REVISION_LOG)
author = revprops.get(core.SVN_PROP_REVISION_AUTHOR)
datestr = revprops.get(core.SVN_PROP_REVISION_DATE)
# Copy the Subversion changes into a new hash, converting them into
# ChangedPath objects.
found_readable = found_unreadable = 0
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
parts = _path_parts(path)
if vclib.check_path_access(self, parts, pathtype, rev):
if is_copy and change.base_path and (change.base_path != path):
parts = _path_parts(change.base_path)
if not vclib.check_path_access(self, parts, pathtype, change.base_rev):
is_copy = 0
change.base_path = None
change.base_rev = None
changedpaths[path] = SVNChangedPath(path, rev, pathtype,
change.base_path,
change.base_rev, action,
is_copy, change.text_changed,
change.prop_changes)
found_readable = 1
else:
found_unreadable = 1
# Return our tuple, auth-filtered: date, author, msg, changes
if found_unreadable:
msg = None
if not found_readable:
author = None
datestr = None
date = _datestr_to_date(datestr)
return date, author, msg, changedpaths.values()
def revinfo(self, rev):
rev = self._getrev(rev)
cached_info = self._revinfo_cache.get(rev)
if not cached_info \
or (include_changed_paths and cached_info[4] is None):
cached_info = _revinfo_helper(rev, include_changed_paths)
if not cached_info:
cached_info = self._revinfo_raw(rev)
self._revinfo_cache[rev] = cached_info
return tuple(cached_info)
def revinfo(self, rev):
return self._revinfo(rev, 1)
return cached_info[0], cached_info[1], cached_info[2], cached_info[3]
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)
if not vclib.check_path_access(self, path_parts1, vclib.FILE, rev1):
raise vclib.ItemNotFound(path_parts1)
if not vclib.check_path_access(self, path_parts2, vclib.FILE, rev2):
raise vclib.ItemNotFound(path_parts2)
if path_parts1:
p1 = self._getpath(path_parts1)
r1 = self._getrev(rev1)
if not vclib.check_path_access(self, path_parts1, vclib.FILE, rev1):
raise vclib.ItemNotFound(path_parts1)
else:
p1 = None
if path_parts2:
p2 = self._getpath(path_parts2)
r2 = self._getrev(rev2)
if not vclib.check_path_access(self, path_parts2, vclib.FILE, rev2):
raise vclib.ItemNotFound(path_parts2)
else:
if not p1:
raise vclib.ItemNotFound(path_parts2)
p2 = None
args = vclib._diff_args(type, options)
def _date_from_rev(rev):
date, author, msg, revprops, changes = self._revinfo(rev)
date, author, msg, changes = self.revinfo(rev)
return date
try:
temp1 = temp_checkout(self, p1, r1)
temp2 = temp_checkout(self, p2, r2)
info1 = p1, _date_from_rev(r1), r1
info2 = p2, _date_from_rev(r2), r2
if p1:
temp1 = temp_checkout(self, p1, r1)
info1 = p1, _date_from_rev(r1), r1
else:
temp1 = '/dev/null'
info1 = '/dev/null', _date_from_rev(rev1), rev1
if p2:
temp2 = temp_checkout(self, p2, r2)
info2 = p2, _date_from_rev(r2), r2
else:
temp2 = '/dev/null'
info2 = '/dev/null', _date_from_rev(rev2), rev2
return vclib._diff_fp(temp1, temp2, info1, info2, self.diff_cmd, args)
except core.SubversionException, e:
_fix_subversion_exception(e)

File diff suppressed because it is too large Load Diff

View File

@@ -1,53 +0,0 @@
PREFACE
-------
This file will go away soon after release 0.8. Please use the SourceForge
tracker to resubmit any of the items listed below, if you think, it is
still an issue:
http://sourceforge.net/tracker/?group_id=18760
Before reporting please check, whether someone else has already done this.
Working patches increase the chance to be included into the next release.
-- PeFu / October 2001
TODO ITEMS
----------
*) add Tamminen Eero's comments on how to make Linux directly execute
the Python script. From email on Feb 19.
[ add other examples, such as my /bin/sh hack or the teeny CGI stub
importing the bulk hack ]
*) insert rcs_path into PATH before calling "rcsdiff". rcsdiff might
use "co" and needs to find it on the path.
*) show the "locked" flag (attach it to the LogEntry objects).
Idea from Russell Gordon <russell@hoopscotch.dhs.org>
*) committing with a specific revision number:
http://mailman.lyra.org/pipermail/viewcvs/2000q1/000008.html
*) add capability similar to cvs2cl.pl:
http://mailman.lyra.org/pipermail/viewcvs/2000q2/000050.html
suggestion from Chris Meyer <cmeyer@gatan.com>.
*) add a tree view of the directory structure (and files?)
*) include a ConfigParser.py to help older Python installations
*) add a check for the rcs programs/paths to viewvc-install. clarify the
dependency on RCS in the docs.
*) have a "check" mode that verifies binaries are available on rcs_path
-> alternately (probably?): use rcsparse rather than external tools
KNOWN BUGS
----------
*) time.timezone seems to not be available on some 1.5.2 installs.
I was unable to verify this. On RedHat and SuSE Linux this bug
is non existant.
*) With old repositories containing many branches, tags or thousands
or revisions, the cvsgraph feature becomes unusable (see INSTALL).
ViewVC can't do much about this, but it might be possible to
investigate the number of branches, tags and revision in advance
and disable the cvsgraph links, if the numbers exceed a certain
treshold.

View File

@@ -1,82 +0,0 @@
Here lie TODO items for the pluggable authz system:
* Subversion uses path privelege to determine visibility of revision
metadata. That logic is pretty Subversion-specific, so it feels like it
belongs outside the vcauth library as just a helper function in viewvc.py
or something. The algorithm is something like this (culled from the
CollabNet implementation, and not expected to work as edited):
# Subversion revision access levels
REVISION_ACCESS_NONE = 0
REVISION_ACCESS_PARTIAL = 1
REVISION_ACCESS_FULL = 2
def check_svn_revision_access(request, rev):
# Check our revision access cache first.
if request.rev_access_cache.has_key(rev):
return request.rev_access_cache[rev]
# Check our cached answer to the question "Does the user have
# an all-access or a not-at-all-access pass?"
if request.full_access is not None:
return request.full_access \
and REVISION_ACCESS_FULL or REVISION_ACCESS_NONE
# Get a list of paths changed in REV.
### FIXME: There outta be a vclib-complaint way to do this,
### as this won't work for vclib.svn_ra.
import svn.fs
rev_root = svn.fs.revision_root(self.repos.fs_ptr, rev)
changes = svn.fs.paths_changed(rev_root)
# Loop over the list of changed paths, asking the access question
# for each one. We'll track whether we've found any readable paths
# as well as any un-readable (non-authorized) paths, and quit
# checking as soon as we know our revision access level.
found_readable = 0
found_unreadable = 0
for path in changes.keys():
parts = _path_parts(path)
kind = request.repos.itemtype(parts, rev)
if kind == vclib.DIR:
access = request.auth.check_dir_access(parts, rev)
elif:
access = request.auth.check_file_access(parts, rev)
if access:
found_readable = 1
else:
found_unreadable = 1
# Optimization: if we've found at least one readable, and one
# unreadable, we needn't ask about any more paths.
if found_readable and found_unreadable:
break
# If there are paths but we can't read any of them, no access is
# granted.
if len(changes) and not found_readable:
request.rev_access_cache[rev] = REVISION_ACCESS_NONE
# If we found at least one unreadable path, partial access is
# granted.
elif found_unreadable:
request.rev_access_cache[rev] = REVISION_ACCESS_PARTIAL
# Finally, if there were no paths at all, or none of the existing
# ones were unreadable, grant full access.
else:
request.rev_access_cache[rev] = REVISION_ACCESS_FULL
return request.rev_access_cache[rev]
The problems are: where does one hang the revision access cache
so that it doesn't survive a given request? On the request, as
shown in the edited code above?
Can we actually get a good interface into the vcauth layer for
asking the all-access / no-access question? Obviously each vcauth
provider can cache that value for itself and use it as it deems
necessary, but ideally the revision access level question will
want to know if said auth provider was able to answer the question
and, if so, what the answer was.
Another off-the-wall idea -- let's just teach Subversion to do
this calculation as part of a libsvn_repos API.

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -1,107 +0,0 @@
RELEASE MANAGEMENT
ViewVC rolls releases from release branches associate with each minor
version of the software. For example, the 1.1.0 is rolled from the
1.1.x branch. The same is true for the 1.1.1, 1.1.2, ... releases.
There is a script, `tools/make-release', which creates a release
directory and the various archive files that we distribute. All other
steps required to get a ViewVC release out of the door require manual
execution (currently by C. Michael Pilato). Those steps are as
follows:
Checkout a working copy of the release branch for the release you
intend to roll, and in that working copy, perform the following steps
(X, Y, and Z below represent integral major, minor, and patch version
numbers, and not literal):
1. Review any open bug reports:
http://viewvc.tigris.org/servlets/ProjectIssues
2. Add a new subsection to the file 'docs/upgrading.html' describing
all user visible changes for users of previous releases of ViewVC.
Commit any modifications. NOTE: This step should not be necessary
for patch releases.
3. Verify that copyright years are correct in both the license-1.html
file and the source code.
4. Update and commit the 'CHANGES' file.
5. Test, test, test! There is no automatic testsuite available. So
just run with permuting different `viewvc.conf' settings... and
pray. Fix what needs fixin', keeping the CHANGES file in sync
with the branch.
6. At this point, the source code committed to the release branch
should exactly reflect what you wish to distribute and dub "the
release".
7. Update your release branch working copy to HEAD.
svn up
8. Edit the file 'lib/viewvc.py' and remove the "-dev" suffix from
__version__. The remainder should be of the form "X.Y.Z", where X,
Y, and Z are positive integers.
*** Do NOT commit this change. ***
9. "Peg" the contributed templates externals definition to the
current HEAD revision:
svn pedit svn:externals .
(squeeze "-rBASE_REV", where BASE_REV is the current HEAD revision
number, between 'templates-contrib' and the target URL).
*** Do NOT commit this change. ***
10. Tag the release:
svn cp -m "Tag the X.Y.Z final release." . ^/tags/X.Y.Z
This will create a copy of the release branch, plus your local
modifications to the svn:externals property and lib/viewvc.py
file, to the tag location.
11. Revert the changes in your working copy.
svn revert -R .
12. Go into an empty directory and run the 'make-release' script:
tools/make-release viewvc-X.Y.Z tags/X.Y.Z
13. Verify the archive files:
- do they have a LICENSE.html file?
- do they have necessary include documentation?
- do they *not* have unnecessary stuff?
- do they install and work correctly?
14. Upload the created archive files (tar.gz and zip) into the Files
and Documents section of the Tigris.org project, and modify the
CHECKSUMS document there accordingly. Also, drop a copy of the
archive files into the root directory of the viewvc.org website
(unversioned).
15. 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.
16. Edit the file 'lib/viewvc.py' again, incrementing the patch number
assigned to the __version__ variable. Add a new empty block in
the branch's CHANGES file. Commit your changes:
svn ci -m "Begin a new release cycle."
17. Edit the Issue Tracker configuration options, adding a new Version
for the just-released one, and a new Milestone for the next patch
(and possibly, minor or major) release. (For the Milestone sort
key, use a packed integer XXYYZZ: 1.0.3 == 10003, 2.11.4 == 21104.)
18. Send to the announce@ list a message explaining all the cool new
features, and post similar announcements to other places interested
in this sort of stuff, such as Freshmeat (http://www.freshmeat.net).

View File

@@ -1,78 +0,0 @@
The following is an email from a developer who was integrating bzr
into ViewVC in which he shares some thoughts on how to further
abstract the version control system interactions into first-class APIs
in the vclib module.
Subject: Re: [ViewCVS-dev] difflib module
Date: Wed, 1 Jun 2005 16:59:10 -0800
From: "Johan Rydberg" <jrydberg@gnu.org>
To: "Michael Pilato" <cmpilato@collab.net>
Cc: <viewcvs-dev@lyra.org>
"C. Michael Pilato" <cmpilato@collab.net> writes:
>> I've tried to minimize the changes to the viewcvs.py, but of course
>> there are a few places where some things has to be altered.
>
> Well, if along the way, you have ideas about how to further abstract
> stuff into the vclib/ modules, please post.
I came up with a few as off now;
* Generalize revision counting; svn starts from 0, bzr starts from 1.
Can be done by a constant; request.repos.MIN_REVNO. For CVS I'm not
sure exactly what should be done. Right now this is only used in
view_revision_svn, so it is not a problem in the short term.
* Generalize view_diff;
* Have a repo-method diff(file1, rev1, file2, rev2, args) that returns
(date1, date2, fp). Means human_readbale_diff and raw_diff does not
have to parse dates. Good for VCS that does not have the date in
the diff. [### DONE ###]
* I'm not sure you should require GNU diff. Some VCS may use own
diff mechanisms (bzr uses difflib, _or_ GNU diff when needed.
Monotone uses its own, IIRC.)
* Generalize view_revision ;
* Have a method, revision_info(), which returns (date, author, msg,
changes) much like vclib.svn.get_revision_info. The CVS version
can raise a ViewCVSException. [### DONE ###]
* Establish a convention for renamed/copied files; current should
work good enough (change.base_path, change.base_rev) but action
string must be same for both svn and others.
* request.repos.rev (or .revision) should give the current revision
number of the repo. No need for this (from view_directory):
if request.roottype == 'svn':
revision = str(vclib.svn.created_rev(request.repos, request.where))
If svn needs the full name of the repo, why not give it when the
repo is created?
* request.repos.youngest vs vclib.svn.get_youngest_revision(REPO)
* More object oriented;
* The subversion backend is not really object oriented. viewcfg.py uses
a lot function from vclib.svn, which could instead be methods of the
Repository class. Example:
diffobj = vclib.svn.do_diff(request.repos, p1, int(rev1),
p2, int(rev2), args)
This should be a method of the repository;
diffobj = request.repos.do_diff(p1, rev1, ...)
I have identified the following functions;
- vclib.svn.created_rev
- vclib.svn.get_youngest_revision
- vclib.svn.date_from_rev
- vclib.svn.do_diff
- vclib.svn.get_revision_info

5
templates-contrib/README Normal file
View File

@@ -0,0 +1,5 @@
This directory contains ViewVC template sets contributed by their
respective authors and expected to work against ViewVC 1.0.x. They
are not necessarily supported by the ViewVC development community, and
do not necessarily carry the same license or copyright as ViewVC
itself.

View File

@@ -0,0 +1,7 @@
Template Set: newvc
Author(s): C. Michael Pilato <cmpilato@red-bean.com>
Compatibility: ViewVC 1.1
The "newvc" template set uses top navigation tabs to flip between
various views of a file or directory, and aims for a clean-yet-modern
look and feel.

View File

@@ -0,0 +1,128 @@
[# Setup page definitions]
[define page_title]Diff of:[end]
[define help_href][docroot]/help_rootview.html[end]
[# end]
[include "include/header.ezt" "diff"]
<form method="get" action="[diff_format_action]" style="display: inline;">
<div>
[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>
<option value="f" [is diff_format "f"]selected="selected"[end]>Full Colored Diff</option>
<option value="u" [is diff_format "u"]selected="selected"[end]>Unidiff</option>
<option value="c" [is diff_format "c"]selected="selected"[end]>Context Diff</option>
<option value="s" [is diff_format "s"]selected="selected"[end]>Side by Side</option>
</select>
<input type="submit" value="Show" />
(<a href="[patch_href]">Generate patch</a>)
</div>
</form>
<div id="vc_main_body">
<!-- ************************************************************** -->
[if-any raw_diff]
<pre>[raw_diff]</pre>
[else]
[define change_right][end]
[define last_change_type][end]
[# these should live in stylesheets]
<table cellpadding="0" cellspacing="0" style="width: 100%;">
[for changes]
[is changes.type "change"][else]
[if-any change_right][change_right][define change_right][end][end]
[end]
[is changes.type "header"]
<tr>
<th class="vc_header" style="width:6%;"><strong>#</strong></th>
<th colspan="2" class="vc_header">
<strong>Line [changes.line_info_left]</strong> |
<strong>Line [changes.line_info_right]</strong>
</th>
</tr>
[else]
[is changes.type "add"]
<tr>
<td id="l[changes.line_number]">[if-any right.annotate_href]<a href="[right.annotate_href]#l[changes.line_number]">[changes.line_number]</a>[else][changes.line_number][end]</td>
<td class="vc_diff_plusminus"><strong style="color: green;">+</strong></td>
<td class="vc_diff_add">[changes.right]</td>
</tr>
[else]
[is changes.type "remove"]
<tr>
<td style="text-decoration: line-through">[changes.line_number]</td>
<td class="vc_diff_plusminus"><strong style="color: red;">&ndash;</strong></td>
<td class="vc_diff_remove">[changes.left]</td>
</tr>
[else]
[is changes.type "change"]
[if-any changes.have_left]
<tr>
<td style="text-decoration: line-through">[changes.line_number]</td>
<td class="vc_diff_plusminus"><strong style="color: yellow;">&lt;</strong></td>
<td class="vc_diff_changes1">[changes.left]</td>
</tr>
[end]
[define change_right][change_right]
[if-any changes.have_right]
<tr>
<td id="l[changes.line_number]">[if-any right.annotate_href]<a href="[right.annotate_href]#l[changes.line_number]">[changes.line_number]</a>[else][changes.line_number][end]</td>
<td class="vc_diff_plusminus"><strong style="color: yellow;">&gt;</strong></td>
<td class="vc_diff_changes2">[changes.right]</td>
</tr>[end]
[end]
[else]
[is changes.type "no-changes"]
<tr><td colspan="3" style="vc_diff_nochange"><strong>- No changes -</strong></td></tr>
[else]
[is changes.type "binary-diff"]
<tr><td colspan="3" class="vc_diff_binary"><strong>- Binary file revisions differ -</strong></td></tr>
[else]
[is changes.type "error"]
<tr><td colspan="3" class="vc_diff_error"><strong>- ViewVC depends on rcsdiff and GNU diff
to create this page. ViewVC cannot find GNU diff. Even if you
have GNU diff installed, the rcsdiff program must be configured
and compiled with the GNU diff location. -</strong></td></tr>
[else][# a line of context]
<tr>
<td id="l[changes.line_number]">[if-any right.annotate_href]<a href="[right.annotate_href]#l[changes.line_number]">[changes.line_number]</a>[else][changes.line_number][end]</td>
<td class="vc_diff_plusminus">&nbsp;</td>
<td style="font-family: monospace; white-space: pre;">[changes.right]</td>
</tr>
[end][end][end][end][end][end][end]
[define last_change_type][changes.type][end]
[end]
[if-any change_right][change_right][end]
</table>
<h3>Diff Legend</h3>
<table class="auto" cellspacing="0">
<tr>
<td class="vc_diff_plusminus"><strong style="color: red;">&ndash;</strong></td>
<td class="vc_diff_remove">Removed lines</td>
</tr>
<tr>
<td class="vc_diff_plusminus"><strong style="color: green;">+</strong></td>
<td class="vc_diff_add">Added lines</td>
</tr>
<tr>
<td class="vc_diff_plusminus"><strong style="color: yellow;">&lt;</strong></td>
<td class="vc_diff_changes1">Changed lines</td>
</tr>
<tr>
<td class="vc_diff_plusminus"><strong style="color: yellow;">&gt;</strong></td>
<td class="vc_diff_changes2">Changed lines</td>
</tr>
</table>
[end]
<!-- ************************************************************** -->
</div>
[include "include/footer.ezt"]

View File

@@ -0,0 +1,139 @@
[# setup page definitions]
[define page_title]Index of:[end]
[define help_href][docroot]/help_[if-any where]dir[else]root[end]view.html[end]
[# end]
[include "include/header.ezt" "directory"]
[if-any where][else]
<!-- you may insert repository access instructions here -->
[end]
<table class="auto">
[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>
</td>
</tr>
[end][end]
</table>
<div id="vc_main_body">
<!-- ************************************************************** -->
<div id="vc_togglables">
[is roottype "svn"]
[if-any rev]r<a href="[revision_href]">[rev]</a>[end]
[else]
[is num_dead "0"]
[else]
[if-any attic_showing]
<a href="[hide_attic_href]">Hide
[else]
<a href="[show_attic_href]">Show
[end]
dead files</a>
[end]
[end]
</div>
<table cellspacing="2" class="fixed" id="dirlist">
<thead>
<tr>
<th style="width: 200px" class="vc_header[is sortby "file"]_sort[end]">
<a href="[sortby_file_href]#dirlist">File
[is sortby "file"]
<img class="vc_sortarrow" alt="[is sortdir "down"](rev)[end]"
width="13" height="13"
src="[docroot]/images/[is sortdir "up"]up[else]down[end].png" />
[end]
</a>
</th>
<th class="vc_header[is sortby "rev"]_sort[end]">
<a href="[sortby_rev_href]#dirlist">Last Change
[is sortby "rev"]
<img class="vc_sortarrow" alt="[is sortdir "down"](rev)[end]"
width="13" height="13"
src="[docroot]/images/[is sortdir "up"]up[else]down[end].png" />
[end]
</a>
</th>
</tr>
</thead>
<tbody>
[if-any up_href]
<tr class="vc_row_odd">
<td colspan="2">
<a href="[up_href]">
<img src="[docroot]/images/back_small.png" alt="" class="vc_icon"
/>&nbsp;../</a>
</td>
</tr>
[end]
[for entries]
[define click_href][is entries.pathtype "dir"][entries.view_href][else][if-any entries.prefer_markup][entries.view_href][else][entries.download_href][end][end][end]
<tr class="vc_row_[if-index entries even]even[else]odd[end]">
<td style="width: 200px" onclick="jumpTo('[click_href]')">
<a name="[entries.anchor]" href="[click_href]" 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>
[is entries.state "dead"](dead)[end]
</td>
<td [if-any entries.log_href]onclick="jumpTo('[entries.log_href]')"[end]>
[if-any entries.rev]
<strong>[if-any entries.log_href]<a href="[entries.log_href]" title="Revision [entries.rev]">[entries.rev]</a>[else][entries.rev][end]</strong>
([entries.ago] ago)
by <em>[entries.author]</em>:
[entries.log]
[is entries.pathtype "dir"][is roottype "cvs"]
<em>(from [entries.log_file]/[entries.log_rev])</em>
[end][end]
[end]
</td>
</tr>
[end]
</tbody>
</table>
<div id="vc_view_summary">
[if-any search_re_form]
<form class="inline" method="get" action="[search_re_action]">
<div class="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 Files" />
</div>
</form>
[if-any search_re]
<form class="inline" method="get" action="[search_re_action]">
<div class="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]
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
[end]
[include "include/pathrev_form.ezt"]
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
[files_shown] file[is files_shown "1"][else]s[end] shown
</div>
[include "include/props.ezt"]
<!-- ************************************************************** -->
</div>
[include "include/footer.ezt"]

View File

@@ -0,0 +1,8 @@
/************************************/
/*** ViewVC Help CSS Stylesheet ***/
/************************************/
body { margin: 0.5em; }
img { border: none; }
table { width: 100%; }
td { vertical-align: top; }
col.menu { width:12em; }

View File

@@ -0,0 +1,126 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>ViewVC Help: Directory View</title>
<link rel="stylesheet" href="help.css" type="text/css" />
</head>
<body>
<table>
<col class="menu" />
<col />
<tr>
<td colspan="2">
<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 />
<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 directory listing view should be a familiar sight to any
computer user. It shows the path of the current directory being viewed
at the top of the page. Below that is a table summarizing the
directory contents, and then comes actual contents, a sortable list of
all files and subdirectories inside the current directory.</p>
<p><a name="summary"></a>The summary table is made up of some or all
of the following rows:</p>
<ul>
<li><a name="summary-files-shown"><strong>Files Shown</strong></a>
- Number of files shown in the directory listing. This might be less
than the actual number of files in the directory if a
<a href="#option-search">regular expression search</a> is in place,
hiding files which don't meet the search criteria. In CVS directory
listings, this row will also have a link to toggle display of
<a href="help_rootview.html#dead-files">dead files</a>, if any are
present.</li>
<li><a name="summary-revision"><strong>Directory
Revision</strong></a> - For Subversion directories only.
Shown as "# of #" where the first number is the most recent
repository revision where the directory (or a path underneath it)
was modified. The second number is just the latest repository
revision. Both numbers are links to
<a href="help_rootview.html#view-rev">revision views</a></li>
<li><a name="summary-sticky-revision-tag"><strong>Sticky
Revision/Tag</strong></a> - shows the current
<a href="help_rootview.html#sticky-revision-tag">sticky revision or
tag</a> and contains form fields to set or clear it.</li>
<li><a name="summary-search"><strong>Current Search</strong></a> -
If a <a href="#option-search">regular expression search</a> is in place,
shows the search string.</li>
<li><a name="summary-query"><strong>Query</strong></a> - Provides
a link to a <a href="help_rootview.html#view-query">query form</a>
for the directory</li>
</ul>
<p><a name="list"></a>The actual directory list is a table with
filenames and directory names in one column and information about the
most recent revisions where each file or directory was modified in the
other columns. Column headers can be clicked to sort the directory
entries in order by a column, and clicked again to reverse the sort
order.</p>
<p>
<!-- If using directory.ezt template -->
File names are links to <a href="help_log.html">log views</a>
showing a list of revisions where a file was modified. Revision
numbers are links to either
<a href="help_rootview.html#view-markup">view</a>
or <a href="help_rootview.html#view-checkout">download</a> a file
(depending on its file type). The links are reversed for directories.
Directory revision numbers are links to <a href="help_log.html">log
views</a>, while directory names are links showing the contents of those
directories.
<!-- If using dir_alt.ezt template -->
<!--
File and directory names are links to retrieve their contents.
File links may be either
<a href="help_rootview.html#view-markup">view</a>
or <a href="help_rootview.html#view-download">download</a> links
depending on the file type. Directory links go to directory
listings. Revision numbers are links to <a href="help_log.html">log
views</a> showing lists of revisions where a file or directory was
modified.
-->
Also, in CVS repositories with the <a
href="help_rootview.html#view-graph">graph view</a> enabled, there
will be small icons next to file names which are links to revision
graphs.</p>
<p>Depending on how ViewVC is configured, there may be more options
at the bottom of directory pages:</p>
<ul>
<li><a name="option-search"><strong>Regular expression
search</strong></a> - If enabled, will show a form field accepting
a search string (a
<a href="http://doc.python.org/lib/re-syntax.html">python regular
expression</a>). Once submitted, only files that have at least
one occurance of the expression will show up in directory listings.
</li>
<li><a name="option-tarball"><strong>Tarball download</strong></a> -
If enabled, will show a link to download a gzipped tar archive of
the directory contents.</li>
</ul>
</td></tr></table>
<hr />
<address><a href="mailto:users@viewvc.tigris.org">ViewVC Users Mailinglist</a></address>
</body>
</html>

View File

@@ -0,0 +1,71 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>ViewVC Help: Log View</title>
<link rel="stylesheet" href="help.css" type="text/css" />
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
</head>
<body>
<table>
<col class="menu" />
<col />
<tr>
<td colspan="2">
<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 />
<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
displayed:
<ul>
<li>The revision number. In Subversion repositories, this is a
link to the <a href="help_rootview.html#view-rev">revision
view</a></li>
<li>For files, links to
<a href="help_rootview.html#view-markup">view</a>,
<a href="help_rootview.html#view-checkout">download</a>, and
<a href="help_rootview.html#view-annotate">annotate</a> the
revision. For directories, a link to
<a href="help_dirview.html">list directory contents</a></li>
<li>A link to select the revision for diffs (see below)</li>
<li>The date and age of the change</li>
<li>The author of the modification</li>
<li>The CVS branch (usually <em>MAIN</em>, if not on a branch)</li>
<li>Possibly a list of CVS tags bound to the revision (if any)</li>
<li>The size of the change measured in added and removed lines of
code. (CVS only)</li>
<li>The size of the file in bytes at the time of the revision
(Subversion only)</li>
<li>Links to view diffs to the previous revision or possibly to
an arbitrary selected revision (if any, see above)</li>
<li>If the revision is the result of a copy, the path and revision
copied from</li>
<li>If the revision precedes a copy or rename, the path at the
time of the revision</li>
<li>And last but not least, the commit log message which should tell
about the reason for the change.</li>
</ul>
<p>
At the bottom of the page you will find a form which allows
to request diffs between arbitrary revisions.
</p>
</td></tr></table>
<hr />
<address><a href="mailto:users@viewvc.tigris.org">ViewVC Users Mailinglist</a></address>
</body>
</html>

View File

@@ -0,0 +1,66 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>ViewVC Help: Query The Commit Database</title>
<link rel="stylesheet" href="help.css" type="text/css" />
</head>
<body>
<table>
<col class="menu" />
<col />
<tr>
<td colspan="2">
<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>

View File

@@ -0,0 +1,166 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>ViewVC Help: General</title>
<link rel="stylesheet" href="help.css" type="text/css" />
</head>
<body>
<table>
<col class="menu" />
<col />
<tr>
<td colspan="2">
<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 />
<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>
</td></tr></table>
<hr />
<address><a href="mailto:users@viewvc.tigris.org">ViewVC Users Mailinglist</a></address>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 337 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 247 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 601 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1004 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 338 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 226 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

View File

@@ -0,0 +1,4 @@
function jumpTo(url)
{
window.location = url;
}

View File

@@ -0,0 +1,332 @@
/*******************************/
/*** ViewVC CSS Stylesheet ***/
/*******************************/
/*** Standard Tags ***/
html, body {
background-color: white;
color: black;
font-family: sans-serif;
font-size: 100%;
margin: 5px;
}
a {
text-decoration: none;
color: rgb(30%,30%,60%);
}
img { border: none; }
table {
width: 100%;
margin: 0;
border: none;
}
td, th {
vertical-align: top;
}
th { white-space: nowrap; }
table.auto {
width: auto;
}
table.fixed {
width: 100%;
table-layout: fixed;
}
table.fixed td {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
form { margin: 0; }
address { font-style: normal; display: inline; }
.inline { display: inline; }
/*** Icons ***/
.vc_icon {
width: 16px;
height: 16px;
border: none;
padding: 0 1px;
}
#vc_header {
padding: 0 0 10px 0;
border-bottom: 10px solid #94bd5e;
}
#vc_footer {
text-align: right;
font-size: 85%;
padding: 10px 0 0 0;
border-top: 10px solid #94bd5e;
}
#vc_topmatter {
float: right;
text-align: right;
font-size: 85%;
}
#vc_current_path {
color: rgb(50%,50%,50%);
padding: 10px 0;
font-size: 140%;
font-weight: bold;
}
#vc_current_path a {
color: rgb(60%,60%,60%);
}
#vc_current_path a:hover {
background-color: rgb(90%,90%,90%);
}
#vc_current_path .thisitem {
color: #94bd5e;
}
#vc_current_path .pathdiv {
padding: 0 0.1em;
}
#vc_view_selection_group {
background: black;
color: white;
margin: 0 0 5px 0;
padding: 5px;
text-align: right;
}
#vc_view_selection_group a {
padding: 5px;
font-size: 100%;
color: white;
text-decoration: none;
}
#vc_view_selection_group a.vc_view_link_this, #vc_view_selection_group a.vc_view_link:hover {
color: #94bd5e;
}
#vc_view_selection_group a:hover {
background-color: black;
}
#vc_view_main {
border-top: 1px solid black;
border-bottom: 1px solid black;
}
#vc_togglables {
text-align: right;
font-size: 85%;
}
#vc_main_body {
background: white;
padding: 5px 0 20px 0;
}
#vc_view_summary {
font-size: 85%;
text-align: right;
margin-top: 5px;
}
/*** Table Headers ***/
.vc_header, .vc_header_sort {
text-align: left;
vertical-align: top;
border-bottom: 1px solid black;
background-color: rgb(80%,80%,80%);
}
.vc_header_sort {
background-color: rgb(85%,85%,85%);
}
/*** Table Rows ***/
.vc_row_even {
background-color: rgb(95%,95%,95%);
}
.vc_row_odd {
background-color: rgb(90%,90%,90%);
}
/*** Directory View ***/
#dirlist td, #dirlist th {
padding: 0.2em;
vertical-align: middle;
}
#dirlist tr:hover {
background-color: white;
}
/*** Log messages ***/
.vc_log {
/* unfortunately, white-space: pre-wrap isn't widely supported ... */
white-space: -moz-pre-wrap; /* Mozilla based browsers */
white-space: -pre-wrap; /* Opera 4 - 6 */
white-space: -o-pre-wrap; /* Opera >= 7 */
white-space: pre-wrap; /* CSS3 */
word-wrap: break-word; /* IE 5.5+ */
}
/*** Properties Listing ***/
.vc_properties {
margin: 1em 0;
}
.vc_properties h2 {
font-size: 115%;
}
.vc_properties td, .vc_properties th {
padding: 0.2em;
}
/*** File Content Markup Styles ***/
.vc_summary {
background-color: #eeeeee;
}
#vc_file td {
border-right-style: solid;
border-right-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_plusminus { width: 1em; }
.vc_diff_remove, .vc_diff_add, .vc_diff_changes1, .vc_diff_changes2 {
font-family: monospace;
white-space: pre;
}
.vc_diff_remove { background: rgb(100%,60%,60%); }
.vc_diff_add { background: rgb(60%,100%,60%); }
.vc_diff_changes1 { background: rgb(100%,100%,70%); color: rgb(50%,50%,50%); text-decoration: line-through; }
.vc_diff_changes2 { background: rgb(100%,100%,0%); }
.vc_diff_nochange, .vc_diff_binary, .vc_diff_error {
font-family: sans-serif;
font-size: smaller;
}
/*** Intraline Diff Styles ***/
.vc_idiff_add {
background-color: #aaffaa;
}
.vc_idiff_change {
background-color:#ffff77;
}
.vc_idiff_remove {
background-color:#ffaaaa;
}
.vc_idiff_empty {
background-color:#e0e0e0;
}
table.vc_idiff col.content {
width: 50%;
}
table.vc_idiff tbody {
font-family: monospace;
/* unfortunately, white-space: pre-wrap isn't widely supported ... */
white-space: -moz-pre-wrap; /* Mozilla based browsers */
white-space: -pre-wrap; /* Opera 4 - 6 */
white-space: -o-pre-wrap; /* Opera >= 7 */
white-space: pre-wrap; /* CSS3 */
word-wrap: break-word; /* IE 5.5+ */
}
table.vc_idiff tbody th {
background-color:#e0e0e0;
text-align:right;
}
/*** Query Form ***/
.vc_query_form {
}

View File

@@ -0,0 +1,51 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<!-- ViewVC :: http://www.viewvc.org/ -->
<head>
<title>ViewVC Exception</title>
</head>
<body>
<h3>An Exception Has Occurred</h3>
[if-any msg]
<p>[msg]</p>
[end]
[if-any status]
<h4>HTTP Response Status</h4>
<p><pre>[status]</pre></p>
<hr />
[end]
[if-any msg][else]
<h4>Python Traceback</h4>
<p><pre>
[stacktrace]
</pre></p>
[end]
[# Here follows a bunch of space characters, present to ensure that
our error message is larger than 512 bytes so that IE's "Friendly
Error Message" won't show. For more information, see
http://oreillynet.com/onjava/blog/2002/09/internet_explorer_subverts_err.html]
</body>
</html>

View File

@@ -0,0 +1,61 @@
[# setup page definitions]
[define page_title]Annotation of:[end]
[define help_href][docroot]/help_rootview.html[end]
[# end]
[include "include/header.ezt" "annotate"]
[include "include/fileview.ezt"]
<div id="vc_main_body">
<!-- ************************************************************** -->
[define last_rev]0[end]
[define rowclass]vc_row_odd[end]
[if-any lines]
<div id="vc_file">
<table cellspacing="0" cellpadding="0">
<tr>
<th class="vc_header">Line</th>
[is annotation "annotated"]
<th class="vc_header">User</th>
<th class="vc_header">Rev</th>
[end]
<th class="vc_header">File contents</th>
</tr>
[for lines]
[is lines.rev last_rev]
[else]
[is rowclass "vc_row_even"]
[define rowclass]vc_row_odd[end]
[else]
[define rowclass]vc_row_even[end]
[end]
[end]
<tr class="[rowclass]" id="l[lines.line_number]">
<td class="vc_file_line_number">[lines.line_number]</td>
[is annotation "annotated"]
<td class="vc_file_line_author">[is lines.rev last_rev]&nbsp;[else][lines.author][end]</td>
<td class="vc_file_line_rev">[is lines.rev last_rev]&nbsp;[else][if-any lines.diff_href]<a href="[lines.diff_href]">[end][lines.rev][if-any lines.diff_href]</a>[end][end]</td>
[end]
<td class="vc_file_line_text">[lines.text]</td>
</tr>
[define last_rev][lines.rev][end]
[end]
</table>
</div>
[else]
[if-any image_src_href]
<div id="vc_file_image">
<img src="[image_src_href]" alt="" />
</div>
[end]
[end]
[include "include/props.ezt"]
<!-- ************************************************************** -->
</div>
[include "include/footer.ezt"]

View File

@@ -0,0 +1,15 @@
[# setup page definitions]
[define page_title]Graph of:[end]
[define help_href][docroot]/help_rootview.html[end]
[# end]
[include "include/header.ezt" "graph"]
<div style="text-align:center;">
[imagemap]
<img usemap="#MyMapName"
src="[imagesrc]"
alt="Revisions of [where]" />
</div>
[include "include/footer.ezt"]

View File

@@ -0,0 +1,70 @@
<div style="border-bottom: solid 1px black;">
<p id="diff">This form allows you to request diffs between any two
revisions of this file.
For each of the two "sides" of the diff,
[if-any tags]
select a symbolic revision name using the selection box, or choose
'Use Text Field' and enter a numeric revision.
[else]
enter a numeric revision.
[end]
</p>
<form method="get" action="[diff_select_action]" id="diff_select">
<table cellpadding="2" cellspacing="0" class="auto">
<tr>
<td>&nbsp;</td>
<td>
[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">
<option value="text" selected="selected">Use Text Field</option>
[for tags]
<option value="[tags.rev]:[tags.name]">[tags.name]</option>
[end]
</select>
<input type="text" size="12" name="tr1"
value="[if-any rev_selected][rev_selected][else][first_revision][end]"
onchange="document.getElementById('diff_select').r1.selectedIndex=0" />
[else]
<input type="text" size="12" name="r1"
value="[if-any rev_selected][rev_selected][else][first_revision][end]" />
[end]
and
[if-any tags]
<select name="r2">
<option value="text" selected="selected">Use Text Field</option>
[for tags]
<option value="[tags.rev]:[tags.name]">[tags.name]</option>
[end]
</select>
<input type="text" size="12" name="tr2"
value="[last_revision]"
onchange="document.getElementById('diff_select').r2.selectedIndex=0" />
[else]
<input type="text" size="12" name="r2" value="[last_revision]" />
[end]
</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>
Type of Diff should be a
<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>
<option value="f" [is diff_format "f"]selected="selected"[end]>Full Colored Diff</option>
<option value="u" [is diff_format "u"]selected="selected"[end]>Unidiff</option>
<option value="c" [is diff_format "c"]selected="selected"[end]>Context Diff</option>
<option value="s" [is diff_format "s"]selected="selected"[end]>Side by Side</option>
</select>
<input type="submit" value=" Get Diffs " />
</td>
</tr>
</table>
</form>
</div>

View File

@@ -0,0 +1,74 @@
<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]
[if-any lockinfo]
<td>Lock status:</td>
<td>[lockinfo]</td>
[end]
[is state "dead"]
<tr>
<td>State:</td>
<td><strong><em>FILE REMOVED</em></strong></td>
</tr>
[end]
[if-any annotation]
[is annotation "binary"]
<tr>
<td colspan="2"><strong>Unable to calculate annotation data on binary file contents.</strong></td>
</tr>
[end]
[is annotation "error"]
<tr>
<td colspan="2"><strong>Error occurred while calculating annotation data.</strong></td>
</tr>
[end]
[end]
[if-any log]
<tr>
<td>Log Message:</td>
<td><pre class="vc_log">[log]</span></td>
</tr>
[end]
</table>

View File

@@ -0,0 +1,10 @@
</div> <!-- vc_view_main -->
<div id="vc_footer">
[if-any cfg.general.address]Administered by <address><a href="mailto:[cfg.general.address]">[cfg.general.address]</a></address><br/>[end]
Powered by <a href="http://viewvc.tigris.org/">ViewVC [vsn]</a>
[if-any rss_href]<br/><a href="[rss_href]" title="RSS 2.0 feed"><img src="[docroot]/images/feed-icon-16x16.jpg" class="vc_icon" alt="RSS 2.0 feed" /></a>[else]&nbsp;[end]
</div>
</body>
</html>

View File

@@ -0,0 +1,63 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<!-- ViewVC :: http://www.viewvc.org/ -->
<head>
<title>[[]ViewVC] [page_title] [if-any rootname][rootname][if-any where]/[where][end][end]</title>
<meta name="generator" content="ViewVC [vsn]" />
<link rel="stylesheet" href="[docroot]/styles.css" type="text/css" />
<script src="[docroot]/scripts.js"></script>
[if-any rss_href]
<link rel="alternate" type="application/rss+xml" href="[rss_href]" title="ViewVC RSS: [if-any rootname][rootname][if-any where]/[where][end][end]">
[end]
</head>
<body>
<div id="vc_header">
<div id="vc_topmatter">
[if-any username]Logged in as: <strong>[username]</strong> |[end]
<a href="[help_href]">ViewVC Help</a>
</div>
<div id="vc_logo">
<a href="http://www.viewvc.org/"><img src="[docroot]/images/viewvc-logo.png" alt="ViewVC logotype" width="240" height="70" /></a>
</div>
<div id="vc_view_selection_group">
[is pathtype "dir"]
<a class="vc_view_link[is view "dir"]_this[end]" href="[view_href]">View Directory</a>
[if-any log_href]
| <a class="vc_view_link[is view "log"]_this[end]" href="[log_href]">Revision Log</a>
[end]
[if-any queryform_href]
| <a class="vc_view_link[is view "queryform"]_this[end]" href="[queryform_href]">Commit Query</a>
[end]
[if-any tarball_href]
| <a class="vc_view_link" href="[tarball_href]">Download Tarball</a>
[end]
[end]
[is pathtype "file"]
<a class="vc_view_link[is view "markup"]_this[end]" href="[view_href]">View File</a>
| <a class="vc_view_link[is view "log"]_this[end]" href="[log_href]">Revision Log</a>
| <a class="vc_view_link[is view "annotate"]_this[end]" href="[annotate_href]">Show Annotations</a>
[if-any graph_href]
| <a class="vc_view_link[is view "graph"]_this[end]" href="[graph_href]">Revision Graph</a>
[end]
| <a class="vc_view_link" href="[download_href]">Download File</a>
[end]
[if-any revision_href]
| <a class="vc_view_link[is view "revision"]_this[end]" href="[revision_href]">View Changeset</a>
[end]
| <a class="vc_view_link[is view "roots"]_this[end]" href="[roots_href]">Root Listing</a>
</div>
<div id="vc_current_path">
[if-any roots_href]<a href="[roots_href]">root</a>[end][if-any nav_path]<span class="pathdiv">/</span>[for nav_path][if-any nav_path.href]<a href="[nav_path.href]">[end][if-index nav_path last]<span class="thisitem">[end][nav_path.name][if-index nav_path last]</span>[end][if-any nav_path.href]</a>[end][if-index nav_path last][else]<span class="pathdiv">/</span>[end][end][end]
</div>
</div> <!-- vc_header -->
<div id="vc_view_main">

View File

@@ -0,0 +1,53 @@
<form method="get" action="[pathrev_action]" style="display: inline">
<div style="display: inline">
[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()">
<option value=""></option>
[if-any branch_tags]
<optgroup label="Branches">
[for branch_tags]
[is branch_tags pathrev]
<option selected>[branch_tags]</option>
[define pathrev_selected][end]
[else]
<option>[branch_tags]</option>
[end]
[end]
</optgroup>
[end]
<optgroup label="Non-branch tags">
[for plain_tags]
[is plain_tags pathrev]
<option selected>[plain_tags]</option>
[define pathrev_selected][end]
[else]
<option>[plain_tags]</option>
[end]
[end]
</optgroup>
[if-any pathrev_selected]
<option selected>[pathrev_selected]</option>
[end]
</select>
[else]
<input type="text" name="pathrev" value="[pathrev]" size="6"/>
[end]
<input type="submit" value="Set Sticky [is roottype "cvs"]Tag[else]Revision[end]" />
</div>
</form>
[if-any pathrev]
<form method="get" action="[pathrev_clear_action]" style="display: inline">
<div style="display: inline">
[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>)
[else]
<input type="submit" value="Clear" />
[end]
</div>
</form>
[end]

View File

@@ -0,0 +1,26 @@
[if-any properties]
<hr/>
<div class="vc_properties">
<h2>Properties</h2>
<table cellspacing="2" class="fixed">
<thead>
<tr>
<th class="vc_header_sort" style="width: 200px;">Name</th>
<th class="vc_header">Value</th>
</tr>
</thead>
<tbody>
[for properties]
<tr class="vc_row_[if-index properties even]even[else]odd[end]">
<td><strong>[properties.name]</strong></td>
[if-any properties.undisplayable]
<td><em>Property value is undisplayable.</em></td>
[else]
<td style="white-space: pre;">[properties.value]</td>
[end]
</tr>
[end]
</tbody>
</table>
</div>
[end]

View File

@@ -0,0 +1,247 @@
[# setup page definitions]
[define page_title]Log of:[end]
[define help_href][docroot]/help_log.html[end]
[# end]
[include "include/header.ezt" "log"]
<table class="auto">
[if-any default_branch]
<tr>
<td>Default branch:</td>
<td>[for default_branch]<a href="[default_branch.href]">[default_branch.name]</a>[if-index default_branch last][else], [end]
[end]</td>
</tr>
[end]
[is pathtype "file"]
[if-any 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]
</td>
</tr>
[end]
[if-any tag_view_href]
<tr>
<td>Links to [pathrev]:</td>
<td>
(<a href="[tag_view_href]">view</a>)
[if-any tag_download_href](<a href="[tag_download_href]">download</a>)[end]
[if-any tag_download_text_href](<a href="[tag_download_text_href]">as text</a>)[end]
[if-any tag_annotate_href](<a href="[tag_annotate_href]">annotate</a>)[end]
</td>
</tr>
[end]
[end]
<tr>
<td>Sticky [is roottype "cvs"]Tag[else]Revision[end]:</td>
<td>[include "include/pathrev_form.ezt"]</td>
</tr>
[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>
<div id="vc_main_body">
<!-- ************************************************************** -->
[define first_revision][end]
[define last_revision][end]
[for entries]
[if-index entries first][define first_revision][entries.rev][end][end]
[if-index entries last]
[define last_revision][entries.rev][end]
<div>
[else]
<div style="border-bottom: 1px dotted black">
[end]
[is entries.state "dead"]
Revision <strong>[entries.rev]</strong>
[else]
<a name="rev[entries.rev]"></a>
[for entries.tag_names]<a name="[entries.tag_names]"></a>
[end]
[for entries.branch_names]<a name="[entries.branch_names]"></a>
[end]
Revision [is roottype "svn"]<a href="[entries.revision_href]"><strong>[entries.rev]</strong></a>[else]<strong>[entries.rev]</strong>[end] -
[if-any entries.view_href]
[is pathtype "file"]
(<a href="[entries.view_href]">view</a>)
[else]
<a href="[entries.view_href]">Directory Listing</a>
[end]
[end]
[if-any entries.download_href](<a href="[entries.download_href]">download</a>)[end]
[if-any entries.download_text_href](<a href="[entries.download_text_href]">as text</a>)[end]
[if-any entries.annotate_href](<a href="[entries.annotate_href]">annotate</a>)[end]
[is pathtype "file"]
[# if you don't want to allow select for diffs then remove this section]
[is entries.rev rev_selected]
- <strong>[[]selected]</strong>
[else]
- <a href="[entries.sel_for_diff_href]">[[]select for diffs]</a>
[end]
[end]
[end]
[if-any entries.vendor_branch]
<em>(vendor branch)</em>
[end]
<br />
[is roottype "svn"]
[if-index entries last]Added[else]Modified[end]
[end]
<em>[if-any entries.date][entries.date][else](unknown date)[end]</em>
[if-any entries.ago]([entries.ago] ago)[end]
by <em>[if-any entries.author][entries.author][else](unknown author)[end]</em>
[if-any entries.orig_path]
<br />Original Path: <a href="[entries.orig_href]"><em>[entries.orig_path]</em></a>
[end]
[if-any entries.branches]
<br />Branch:
[for entries.branches]
<a href="[entries.branches.href]"><strong>[entries.branches.name]</strong></a>[if-index entries.branches last][else],[end]
[end]
[end]
[if-any entries.tags]
<br />CVS Tags:
[for entries.tags]
<a href="[entries.tags.href]"><strong>[entries.tags.name]</strong></a>[if-index entries.tags last][else],[end]
[end]
[end]
[if-any entries.branch_points]
<br />Branch point for:
[for entries.branch_points]
<a href="[entries.branch_points.href]"><strong>[entries.branch_points.name]</strong></a>[if-index entries.branch_points last][else],[end]
[end]
[end]
[if-any entries.prev]
[if-any entries.changed]
[is roottype "cvs"]
<br />Changes since <strong>[entries.prev]: [entries.changed] lines</strong>
[end]
[end]
[end]
[if-any entries.lockinfo]
<br />Lock status: [entries.lockinfo]
[end]
[is roottype "svn"]
[if-any entries.size]
<br />File length: [entries.size] byte(s)
[end]
[if-any entries.copy_path]
<br />Copied from: <a href="[entries.copy_href]"><em>[entries.copy_path]</em></a> revision [entries.copy_rev]
[end]
[end]
[is entries.state "dead"]
<br /><strong><em>FILE REMOVED</em></strong>
[else]
[is pathtype "file"]
[if-any entries.prev]
<br />Diff to <a href="[entries.diff_to_prev_href]">previous [entries.prev]</a>
[if-any human_readable]
[else]
(<a href="[entries.diff_to_prev_href]&amp;diff_format=h">colored</a>)
[end]
[end]
[is roottype "cvs"]
[if-any entries.branch_point]
, to <a href="[entries.diff_to_branch_href]">branch point [entries.branch_point]</a>
[if-any human_readable]
[else]
(<a href="[entries.diff_to_branch_href]&amp;diff_format=h">colored</a>)
[end]
[end]
[if-any entries.next_main]
, to <a href="[entries.diff_to_main_href]">next main [entries.next_main]</a>
[if-any human_readable]
[else]
(<a href="[entries.diff_to_main_href]&amp;diff_format=h">colored</a>)
[end]
[end]
[end]
[if-any entries.diff_to_sel_href]
[if-any entries.prev], [else]<br />Diff[end]
to <a href="[entries.diff_to_sel_href]">selected [rev_selected]</a>
[if-any human_readable]
[else]
(<a href="[entries.diff_to_sel_href]&amp;diff_format=h">colored</a>)
[end]
[end]
[end]
[end]
<pre class="vc_log">[entries.log]</pre>
</div>
[end]
<!-- ************************************************************** -->
</div>
[is pathtype "file"]
[include "include/diff_form.ezt"]
[end]
[include "include/footer.ezt"]

View File

@@ -0,0 +1,18 @@
[# setup page definitions]
[define page_title]View of:[end]
[define help_href][docroot]/help_rootview.html[end]
[# end]
[include "include/header.ezt" "markup"]
[include "include/fileview.ezt"]
<div id="vc_main_body">
<!-- ************************************************************** -->
<div id="vc_markup"><pre>[markup]</pre></div>
[include "include/props.ezt"]
<!-- ************************************************************** -->
</div>
[include "include/footer.ezt"]

Some files were not shown because too many files have changed in this diff Show More