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

Compare commits

..

375 Commits
V0_4 ... V0_9_4

Author SHA1 Message Date
(no author)
fd44add349 This commit was manufactured by cvs2svn to create tag 'V0_9_4'.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/tags/V0_9_4@1076 8cb11bc2-c004-0410-86c3-e597b4017df7
2005-08-17 20:45:14 +00:00
pefu
44528c0b7e quick backport to Python 1.5.2
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@427 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-24 11:38:30 +00:00
pefu
11f594b7ed Using 0.10 as a version number AFTER 0.9 is a really bad idea and
will cause a lot of confusion, because many people will think 0.10 < 0.9!

Since the main functionality of ViewCVS is mature, there is no need to
avoid bumping up the version up to 1.0-dev.  I do that right now.

Merry Xmas to all.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@426 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-23 15:54:18 +00:00
gstein
8a9719a5f8 we're developing 0.10 now...
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@425 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-23 12:04:32 +00:00
gstein
7ca272ab88 updates for 0.9
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@423 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-23 11:26:46 +00:00
gstein
d13e98ef34 rolling 0.9
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@422 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-23 11:15:50 +00:00
gstein
ba99093be8 fix the test for selecting the "Side by Side" popup
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@421 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-23 10:34:14 +00:00
gstein
9171ad07a8 Add a missing UTC onto the revision's date.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@420 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-23 09:58:33 +00:00
gstein
cd03714ecd Stop defining page titles in the code, and do it in the templates
instead.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@419 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-23 09:57:06 +00:00
gstein
df09de394d Add arguments to [include] directives. This allows the caller to pass
in additional arguments to the target EZT file. The target file can
refer to the arguments as [arg0] thru [argN].

Example usage: [include "header.ezt" "annotate"]


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@418 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-23 09:47:26 +00:00
gstein
f46fed363e Provide for a way to internationalize the "ago" times in the pages.
(need to put instructions somewhere, but in brief:)

An admin can create a KV file in the [i18n] domain:

kv_files = [i18n]data/%lang%-text.conf

That KV file should have a [time] section and options named
%(name)_singular and %(name)_plural, where name is one of: second,
minute, hour, day, week, month, year. Each of the values should be
like: %d second. Additionally, one option name "little_time" with a
value like: very little time  (e.g. no format specifier).


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@417 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-23 07:07:14 +00:00
gstein
b41bd99aa3 Fix a bug where accidentally overwrote [nav_path] with the file
navigation. Change the file navigation to [nav_file] (in the code and
the template)


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@416 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-23 04:47:18 +00:00
gstein
c9710b9ba2 remove the "diff_name" variable; it is unused and we don't want
(language-specific) text in the source.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@415 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-23 04:18:42 +00:00
gstein
5ca27eee4c Revise some of the attic handling to clarify what is happening, some
of the naming, and shift some text from the program to the templates.

* viewcvs.conf.dist: clarify description of the hide_attic default

* viewcvs.py: add [show_attic_href], [hide_attic_href], and
    [rows.state] variables for managing the attic links. remove the
    [rows.hide_attic_href] and [rows.attic] variables.

* directory.ezt, dir_alternate.ezt: use the new vars to insert the
    text/links for the attic handling.

* upgrading.html: document the template variable changes. a few other
    tweaks.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@414 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-23 04:13:35 +00:00
gstein
caf33e7862 Misc updates:
* adjust the timers to take a tag on the start; this allows them to be
  started/stopped independently, rather than needing to nest them

* add a few timers to get some perf info

* removed unused stuff: header_comment and html_link()

* use request.amp_query for the tarball_href


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@413 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-22 11:16:05 +00:00
gstein
1470ccd12f Various UI fixes and tweaks
* viewcvs.py:
  - make "search" param a sticky variable; it should propagate through
    the screens, across column sort changes, etc
  - htmlify() the search expression
  - add a new variable to specify whether a search form is needed on
    the directory page (to select a tag and/or a regex)
  - add [files_shown] since [num_files] isn't "right"; need longer
    term fix for variable w.r.t message when zero files are selected
  - always create form parameters because we may have a search form
    (and now use them in the search form)
  - use new ezt.boolean() where appropriate

* directory.ezt, dir_alternate.ezt:
  - display the current location and selection criteria in one block
  - move selection form to bottom of screen; this emphasizes the
    directory contents rather than how to select/refine the contents
  - tweak the "no_match" message since the search expression could be
    causing the zero-matches
  - use a table to format the various forms of selection criteria
  - use the params for *both* tag selection and regex selection
  - optionally display the "show all files" button, based on whether
    there is current selection criteria or not


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@412 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-22 04:54:38 +00:00
gstein
b476f4a882 add a new utility function for creating a "boolean" value
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@411 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-22 04:43:41 +00:00
gstein
eacb04ee26 Whoops! Get the right template for the "markup" page.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@410 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-22 04:15:50 +00:00
gstein
2910bc4a22 If more than one argument is given to the implied PRINT, then treat it
as a formatting command. Prepare the arguments and pass them to the
new _cmd_format function.

_cmd_format splits up the format/value and then delivers it to the
output file with any potential substitutions.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@409 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-22 02:29:42 +00:00
gstein
ae049f281b Implement the alternate view: revnum in front, linking to log page;
fname in second column, linking to HEAD.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@408 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-21 12:22:12 +00:00
gstein
7e3baffea4 add dir_alternate.ezt
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@407 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-21 11:59:45 +00:00
gstein
c40b88fb8b Remove the flip_links toggle and add a radio button for dir_alternate.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@406 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-21 11:11:51 +00:00
gstein
c7fc7d8885 Oops. main_title is still used by the templates. We could probably
eliminate it, but keep it for now.

Found by: Peter Funk <pf@artcom-gmbh.de>


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@405 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-21 10:59:46 +00:00
gstein
68336ffcdf Move the set of the name to *after* we strip the Attic/ from it. Prior
to this, when we displayed the attic contents, then would have Attic/
on the front of the name (erroneously).


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@404 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-21 10:11:00 +00:00
gstein
70db664ec6 Clean up the mess in directory.ezt
* rather than using [headers] to determine the columns, just display
  them explicitly. use the new [have_logs] to determine whether the
  additional columns are needed.

* fix a bunch of problems in directory.ezt relating to unreadable
  files or errors while reading CVS information (lining up columns and
  whatnot)

* remove the "show_author" config option, favoring removal from the
  templates instead.

* remove the unused "hide_non_readable" option. remove the
  "use_java_script" and "open_extern_window" options; these were
  removed recently, but accidentally left in the .conf file.

* remove the "flip_links_in_dirview" option, favoring a new template
  instead. added a copy of directory.ezt as dir_alternate.ezt. changes
  to actually make the columns flipped are coming in a future commit.

* remove the [rev_in_front], [rows.span], and [rows.cols] options;
  these are now obsolete given the above changes

* add documentation to upgrading.html


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@403 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-21 04:50:33 +00:00
timcera
e88fcaebce Added lib/accept.py
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@402 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-21 04:46:11 +00:00
pefu
e2c82d9341 Bugfix: Exception handling: html_footer(None) passes a None-request
to generate_page()


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@401 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-20 10:19:36 +00:00
gstein
3844161f7d add a little commented-out print statement for debugging
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@400 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-20 00:25:45 +00:00
gstein
9da5df779a Add capability for a couple forms of internationalization.
* define a new "languages" config item for the available languages

* use the accept.py module to select the appropriate language based on
  the Accept-Language header and the languages config.

* add a new key/value files concept. load them after determining the
  language to use.

* support %lang% specification in the template files. This required
  passing the "request" variable to generate_page() and to
  html_footer()

* pass the 'kv' name into all templates


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@399 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-20 00:18:12 +00:00
gstein
f9d697db0c Add some code to parse and process the Accept-Language header. We'll
be using this to dynamically select what language to use for page
generation.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@398 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-19 05:09:21 +00:00
timcera
8432cb1c1d An [end] directive was missing, ViewCVS was throwing the new unclosed block
error.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@397 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-18 06:11:13 +00:00
timcera
c8718c00cd Added new template files.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@396 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-18 06:07:47 +00:00
gstein
d71c208540 Big update! Convert the rest of the pages to use templates.
* annotation, human readable diff, CvsGraph, and a marked-up file all
  use a template to generate the basic parts of the page. In many
  cases (annotate, graph, markup), the content is still rendered
  by other code.

* for the diff support, we take advantage of the new list iteration
  support in EZT to "iterate on" a DiffSource object, which parses a
  diff stream into structured object for the template to display.

* for the CvsGraph page, we make use of EZT's new stream support to
  stream in an imagemap from the CvsGraph executable. the graph page
  includes a navigation header now.

* updated the upgrading.html page with some items missing from the 0.7
  instructions, plus new instructions.

* because of the complete conversion, the configuration file no longer
  specifies colors or images -- these are all in the templates now.
  Removed the relevant material from config.py and viewcvs.conf.dist.

* converted html_footer() to use the new footer.ezt template because
  we still have to call that manually in the markup and annotate
  cases.

* add the templates to viewcvs.conf.dist and config.py

* added a handy generate_page() utility for generating pages from a
  template and its input data.

* removed download_link, html_icon, html_option, print_diff_select,
  and navigate_header utility functions. These all printed HTML to the
  output, but are obsolete due to the new templates.

* the new nav_header_data() utility function fills in data used by the
  "header.ezt" template, which replaces the navigate_header() func.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@395 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-18 00:22:14 +00:00
gstein
830c6ba76c Move the footer to its own template, and include it from the others.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@394 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-18 00:09:00 +00:00
gstein
e9a4e3bbe2 Minor fix to the diff-selection popup. It tested for "c" when it
should have been "s". (the resulting bug is that a side-by-side diff
would never appear pre-selected in the popup)


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@393 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-18 00:06:53 +00:00
gstein
62f37986d5 Some more refinements:
* [include] now uses the current template's directory as a base for
  including other templates. Thus, you can [include "header.ezt"] to
  refer to a header.ezt template in the same directory.

* _cmd_print() now looks for a "read" attribute on the value, to
  determine whether it is a stream (rather than a simple string). It
  can now copy a stream to the output.

* added a simple catch for unclosed blocks. It doesn't say *what* is
  unclosed, but it will at least give you an error about it, rather
  than quiet failure.

* [for] loops now iterate until the end of a list (determined
  internally by the interpreter when an IndexError occurs), rather
  than using len(list). This allows for lists of an indeterminate
  length to be used.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@392 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-18 00:03:53 +00:00
timcera
a2b579b5ac Allow the installer to view differences between old and new configuration files.
* Use lib/ndiff.py to do the differencing.
* Passes by configuration files that have no differences between new and old.
* Create menu loop to choose between overwrite, do not overwrite and view
  differences.

Future:
  Need to work on difference format, ndiff is unusual.  Need line numbers.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@391 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-16 01:58:00 +00:00
gstein
46d8987848 Changes to directive argument parsing.
1) don't include the surrounded [ ] in the regex group, this avoids the need
   to slice them off (the [1:-1] stuff)
2) use a new regex to properly parse the args: loose double quotes, strict
   symbols.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@390 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-15 03:37:59 +00:00
gstein
73209134c9 minor change to reduce a bit of code: use copy_stream() where possible
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@389 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-15 03:36:36 +00:00
gstein
8e9831d6ec Enhance the directive parsing to allow anything between double quotes, and
to be restrictive on what a refname can contain.

Add some basic tests for the parsing regex.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@388 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-13 12:46:09 +00:00
pefu
83f6a7c7d5 Install new help pages also
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@387 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-12 20:43:58 +00:00
pefu
acbf138a6e Add Help buttons.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@386 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-12 20:42:31 +00:00
pefu
e872681988 More help pages and improved navigation between help pages.
help_logtable not yet finished (sorry, I will try to do this later).


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@385 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-12 14:14:54 +00:00
pefu
27430791cb Added a new toggle to the Tkinter GUI allowing to play around with
Tim Ceras new 'use_re_search' configuration option.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@384 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-12 12:29:51 +00:00
gstein
e72b133665 Add warnings about the regex-based searching option.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@383 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-12 11:48:21 +00:00
gstein
3ea9aec854 Fixes for the regex searching.
* get_file_tests(): revert a portion: on error, append file, not full_name

* process_checkout(): take individual params rather than a request object.
    nit: removed a call to normpath()

* view_checkout(): pass the individual params to process_checkout()

* search_files():
  - call it "request" for consistency with other uses of the object
  - DON'T CHANGE ITS FIELDS. the request object is passed to the template,
    which needs values from it. we cannot go mucking with it. and who knows
    what the caller may want to do with it.
  - set up various local vars rather than munging request.*. note that we
    call mimetypes.guess_type() manually
  - rearrange ordering within loop to avoid expensive operations if we are
    just going to bail out: if we have a dir, or the file isn't a text file.
  - style nits: spaces after commas in function calls' args
  - if we terminate reading of a pipe, then close it (avoids a hang)
  - pass individual params to process_checkout()


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@382 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-12 11:42:13 +00:00
gstein
a807605e31 Minor tweaks on the way to i18n.
* _prepare_ref() and _get_value() can now handle string constants
* change [is] processing to stop special-casing strings
* single quotes are not allowed, so simplify [include] parsing


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@381 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-12 09:50:37 +00:00
gstein
d3ed0a9cb5 add use_re_search default
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@380 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-12 09:47:27 +00:00
pefu
77f1d6f2e4 ViewCVS 0.8 is out and development for 0.9 started
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@379 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-11 11:52:59 +00:00
timcera
d59be9459d * Added regular expression search of all files in a directory.
+ created search_files function
  + split view_checkout into view_checkout and process_checkout.
    Wanted the process_checkout code to checkout the files from the
    repository.
  + split get_file_data into get_file_data and get_file_tests.
    Wanted get_file_tests to tell what kind of object was in the repository.
  + Modified directory.ezt to include searches.
    - Moved select by tag function up to the top of the page
    - Included code to return how many files survived the filters
    - The 'Reset' button now says 'Show all files'.
  + Modified viewcvs.conf.dist to include use_re_search option.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@378 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-11 05:19:22 +00:00
gstein
1a8c6ac15b grr... prevent double compression of the tarball
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@377 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-10 20:02:54 +00:00
gstein
4f5800cec5 ViewCVS 0.8 is out, so toss the warning.
All the options were removed, so stop repeating that fact.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@375 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-10 13:45:35 +00:00
gstein
e4cf54b49e More edits.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@374 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-10 13:44:02 +00:00
gstein
24070dcc6f * <a> cannot span <h2>, so move the anchors inside the <h2> tags
* colorization is "less important" than the other subjects, so shift
  it to the bottom of the page (no changes)
* reflow one paragraph (no changes)


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@373 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-10 13:29:21 +00:00
gstein
afe213e140 Prep for the 0.8 release: shift around the feature listing, adjust the
links to refer to 0.8, add a link for the .zip file, add links for the
CHANGES file.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@372 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-10 13:23:34 +00:00
gstein
0dd6919af9 Describe the changes (at a higher level than a ChangeLog) that have
been introduced into ViewCVS 0.8.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@371 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-10 13:05:59 +00:00
gstein
1b4543ed92 Remove the even_odd option; it is no longer used.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@370 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-10 09:42:18 +00:00
pefu
26259d1fb1 Added a help page for the query database screen.
Added navigation between help pages.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@369 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-08 22:03:16 +00:00
pefu
6941197684 New wording that should work for either setting of flip_links_in_dirview.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@368 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-08 21:37:29 +00:00
timcera
02b0870390 * Removed requirement for trailing blanks on paths in viewcvs.conf.
In fact, can have them or not, doesn't matter.
* Changed logic dealing with collecting the revision number from the view_tag
  for rlog.  Identical 'rlog=...' statements were in both the if and else
  blocks.
* Added "'.for': 'fortran'" to list of extensions for enscript.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@367 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-07 14:43:42 +00:00
gstein
715f4dfa5a rolling 0.8
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@366 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-07 11:37:00 +00:00
gstein
5e5ee35046 Simplify generation of header using simple interpolation, rather than
depending upon locals()  (that function is generally Badness)

Removed the meta fields.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@365 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-07 11:22:25 +00:00
pefu
77355718c6 Fix SF Bug [#489470] loginfo-handler broken due to rlog interface change
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@364 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-05 19:00:27 +00:00
pefu
19ab90b4fb note modified to take new templates into acount
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@363 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-05 14:57:41 +00:00
pefu
7a3c8bf9c4 Fix title string in cvsgraph output: path was missing. module is unsed by ViewCVS
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@362 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-12-05 14:52:59 +00:00
pefu
d11dffb713 Table header should look left aligned and green similar
to the table headers in directory.ezt and query.ezt


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@361 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-27 22:13:30 +00:00
pefu
16281c1db5 A lot of small changes in one commit:
lib/viewcvs.py, templates/directory.ezt:
* Fix SF Bug [ #486085 ] checkout_magic_path uses invalid chars
  Provide backward compatibility for old style URLs.

standalone.py:
* Changed from calling viewcvs.main() to calling viewcvs.runcgi(),
  so that the timing results sampled by debug are displayed in the browser.

* Added two more toggles to the GUI.  Now it is simpler to perform a
  systematic test of several permutations of these config flags.

* Shortened the label text for the query entry field in the standalone GUI.

* Added some comments to this Tkinter GUI setup code, which has grown beyond
  than what I was my first intention.

lib/viewcvs.py, lib/config.py, cgi/viewcvs.conf.dist, standalone.py
and templates/directory.ezt:
* Added a new config flag variable called flip_links_in_dirview, which defaults
  to the classic setting (like cvsweb).  If it is set to 1 instead then the
  file and revision columns are exchanged and at the same time these two columns
  exchange the meaning of their links.  This design should solve the user
  interface design problem brought up first in
    http://www.lyra.org/pipermail/viewcvs-dev/2001-November/000285.html
  which was than than further discussed here:
    http://www.lyra.org/pipermail/viewcvs-dev/2001-November/000288.html
    http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/viewcvs/viewcvs/templates/Attic/direasy.ezt?hideattic=0
    http://www.lyra.org/pipermail/viewcvs/2001q4/000533.html
  Since than I received two personal emails from people sharing my POV.

  Unfortunately the huge [for rows]-block in directory.ezt has grown
  again.  But it is difficult to avoid this with the current design of
  lib/viewcvs.py and the templating engine lib/ezt.py.  I'm open to
  suggestions.

  In lib/viewcvs.py function view_directory I factored out the computation
  of row.span into a new local variable called span, since the number of
  empty trailing columns is decreased by one if the flip_links layout
  is configured.

templates/query.ezt:
* Rearranged the table columns in the commit database query result table,
  so that they match the existing table layouts of directory.ezt and
  log_table.ezt.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@360 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-27 21:56:06 +00:00
pefu
0da8dec361 * cutting a release should become even easier: Added tarball and .zip file
generation. (People using Windows often still prefer .zip over .tar.gz)
  fix permissions before generating the files.  Don't emit a an irritating
  warning, if someone tries this script to cut out viewcvs-0.7 again (even if
  solely done to test this script, before the v0_8 tag has been set ;-))


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@359 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-27 21:18:39 +00:00
pefu
1606e4e6dd Add entry field for query.ezt
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@358 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-19 22:52:02 +00:00
pefu
0393ef47f7 synchronize lib/config.py with viewcvs.conf.dist.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@357 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-19 21:25:01 +00:00
pefu
3d9eb3c638 all externals links in italic.
Added more subsections.  Especially a procedure about what I
think how Greg is going to cut a ViewCVS release.
suggestions and corrections welcome.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@356 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-19 21:13:43 +00:00
pefu
6a0c6f2029 Log messages should look like they were
formatted in the first place. (see viewcvs.print_log() last line for
example, how the log message is displayed elsewhere


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@355 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-19 14:36:18 +00:00
timcera
b564bd020a * Added apache_icons.py so that standalone.py works.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@354 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-19 13:55:18 +00:00
pefu
889f71f657 Even on the 1600x1200 pixel display of my new DELL Inspiron the table
generated by query.cgi was unacceptable wide.

After I widened the browser window so that it occuoies the whole
screen width, it still required a horizontal scrollbar.
I find this unusable.  So I decided to remove the
Description column and put the Log message blocks on a separate
table row.

But since this may be a matter of taste, I left the original
stuff in, but commented out.  People with really large
screens (2000 pixels and up) might want to revert this change.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@353 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-19 12:09:55 +00:00
pefu
84188fafbb After cvs updating I got
Python Exception Occurred ... template.parse_file(os.path.join(viewcvs.g_template_dir,
AttributeError: g_template_dir

in query.py.  This was caused by the rename in viewcvs.py from 1.64 to 1.65
This was easy to find and easy to fix. ;-)
ViewCVS is great tool


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@352 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-19 11:15:18 +00:00
pefu
eff0ddeabe Add the new debug module to the installer
installed viewcvs dies with an ImportError exception otherwise.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@351 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-19 10:22:28 +00:00
gstein
d69802487f Revamp bin2inline to create lines that are less than 80 characters wide.
Regenerate the icons.

Suggested by: Peter Funk <pf@artcom-gmbh.de>


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@350 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-19 10:13:01 +00:00
gstein
0ff4aed795 Various changes:
* security: remove use of SimpleHTTPServer and just go with Base

* get the icons working -- send a 200 response line

* get redirects working by sending Location: header

* toss the PATH_TRANSLATED so we don't have to depend on .translate_path()

* use normal Python style for base invocation, rather than the self.base
  thing

* use SO_REUSEADDR if it is defined to avoid problems with stop/start of the
  server and rebinding to the socket


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@349 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-19 04:36:19 +00:00
gstein
a4aa00b5f1 Just store the icons as binary strings rather than worrying about base64.
Both are equally unintelligible, so we may as well simplify.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@348 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-19 04:20:04 +00:00
gstein
cef439958f Remove direasy.ezt.
directory.ezt incorporated its cvsgraph icon, so its only difference is
flipping the links around. But the existing definition for the links makes
more sense, so that leaves direasy.ezt without any new or interesting
features.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@347 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-19 03:11:21 +00:00
gstein
175d6f0ac4 Updates to the help stuff
* rename ~helppage~ magic path to ~docroot~ with the intent that we might
  serve other content out of there (including users' data if necessary)

* shift Help link down below the logo where it is more visible

* rename g_template_dir to its real semantic: g_install_dir

* rename view_helppage() to view_doc() and use new g_install_dir to find the
  doc root.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@346 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-19 02:59:13 +00:00
gstein
27df7480c5 Various tweaks:
* remove standalone comment from README; standard operation is to use a CGI
  script, so we don't want an incorrect emphasis; keep this discussion in
  INSTALL.

* clarify the GUI vs CGI operation description at the head of the file

* other updates/clarifications in INSTALL

* reformat a bit of text in the installer


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@345 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-19 01:10:08 +00:00
pefu
05bec0ce73 Advertise standalone.py ;-). And some cosmetic fixes
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@344 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-18 20:27:26 +00:00
pefu
d7c84f3149 Fix unit test to take the new white space compression into
account.  Using this opportunity to update another not related
part of the module doc string.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@343 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-18 20:03:19 +00:00
pefu
a0ece2036e Remove a superflous line of code and change the redirect response.
This should fix the first two of the five remarks made
by Greg Stein yesterday


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@342 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-18 14:04:41 +00:00
gstein
fcec542cfb Add a new debug module for developers to do timing/profiling and similar
tasks. Add a bit of instrumentation for EZT template parsing.

Add whitespace reduction to EZT. On my box, this doubles the parse time of
directory.ezt from .03 to .06 seconds, and log.ezt from 0.047 to 0.1
seconds. (about double in both cases) My two samples showed a reduction in
the response size by about 25%.

Instrument the overall execution of ViewCVS to examine relative percentage
of template parsing.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@341 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-18 12:54:40 +00:00
gstein
37166f065c Remove the <tt> tags. They actually didn't force fixed-width when
diff_font_face said otherwise. Further, fixed-width isn't necessarily
"correct" here. Most people (SourceForge, Debian, ASF, handhelds.org, at
least) all run with proportional fonts.

Admins can set diff_font_face to switch to fixed-width.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@340 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-18 11:52:59 +00:00
gstein
983a2c298e Minor tweaks:
- "view annotated" rather than "view annotation" (we don't want to imply
  there is some kind of annotation facility; we really need a better word
  for this; and no... 'blame' isn't it :-)

- use a rowspan on the revision column


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@339 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-18 11:45:54 +00:00
gstein
51988428f2 Fix up the diff links:
* put them back on a single line

* restore ordering -- "previous" is the most common, so it goes first

* add the action text into Diff links for larger hit targets
  (idea from Stephen Lamm <slamm@yahoo-inc.com>)


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@338 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-18 11:30:27 +00:00
gstein
9a8c28d886 Shift some presentation code out of query.py; do the work in the template.
Also, make sure the result table isn't shown if we haven't run a query.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@337 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-18 11:11:38 +00:00
gstein
02db1bf262 Two nits: add some closing quotes to fix Emacs' font-lock; shift some code
outside of a try/except block (the try/except shouldn't apply).


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@336 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-18 10:36:45 +00:00
gstein
b048907b6d Complete the conversion of the cvsdb query facility over to templates. This
was a "minimal impact" switch. A bit more cleanup is coming separately.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@335 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-18 10:34:35 +00:00
gstein
5e497355a4 Fix up how the cvsdb handles loading the configuration.
* cvsdb.py: provide for running out of the CVS working dir. pass the config
  to GetRlogData().

* rlog.py: stop reading the config here (it's a library module). accept the
  config object as a param.

* viewcvs-install: turn off install-time substitution for several files


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@334 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-18 10:29:31 +00:00
gstein
45d1849883 Enable [if-index REF first]
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@333 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-18 10:22:36 +00:00
pefu
1e082e2982 Bugfix: stdout redirection problem, which occured with enscript
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@332 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-17 22:41:01 +00:00
pefu
55a94583fa I've rewritten the handling of icons in 'standalone.py': Currently only
very few icons from the set of icons distributed with Apache are really
used within the ViewCVS templates.  I've simply converted them into
a tiny new module, which is imported and used by standalone.py.  Now
standalone.py is really 'standalone' and can be run without Apache
alltogether.  The former options '-i' and '--apache_root=' of standalone.py
are now obsolete and I've removed them again.

The tool 'bin2inline_py.py' that I have used to convert the icon files
into a single sourcecode file is a cut-and-rape-oneway script from
something I've written a while ago to put icons into some other Tkinter
standalone program.  I case someone wants to add another icon, I've
placed this hacked version into the tools subdirectory and modified
'make-release', so that it will not be included into the final
ViewCVS 0.8 tarball.

For those who want to run standalone directly from the ViewCVS
source distribution, I've added a new option '-r' (or --repository=)
to standalone.py, so that it is now possible to patch the default
path '/home/cvsroot' without having to setup a Symlink in /home
pointing to their repository.  (Not everybody has the permissions
to do that on their server.)


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@331 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-17 21:53:03 +00:00
pefu
cb41e82e6b This hopefully corrects a mistake I made. The alternative would have
been to change all references to LICENSE.html sprinkled
over all source files.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@330 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-17 13:09:58 +00:00
pefu
2b2f813262 Add comment about policy to keep external links in italic
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@329 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-17 12:27:01 +00:00
gstein
679153a77c Use a more sophisticated pipe so that we can get the output of the MySQL
command. In the success case, nothing is printed. But for the error case, we
get the information about the error.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@328 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-17 11:20:08 +00:00
gstein
674a2ee494 incorporate the graph icon from direasy (add size params). keep the links
the same. eliminate the border around the image.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@327 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-17 09:25:19 +00:00
gstein
8930cd8736 * use 1% for the width to keep the column as skinny as possible
* use &nbsp; for spaces in the links on the left side (compensating for the
  above change)
* Open Source has a space in it :-)
* offsite links are italicized


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@326 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-17 08:58:30 +00:00
pefu
bef73fd21a Improve error handling. Thanks to Tim Cera.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@325 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-16 14:52:06 +00:00
timcera
9fdfe05d24 * Corrected the colspan option for the 'Log' row.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@324 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-15 20:42:25 +00:00
timcera
70b05c8a5e * Added direasy.ezt to install.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@323 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-15 20:17:16 +00:00
pefu
2bc61a621d direasy.ezt is a simple variation based on directory.ezt.
Recently Stephen Lamm wrote:
> Have file links go to the latest revision instead of the cvs log.
> When I click on the file name, I expect to get that file, and not
> some meta information about it. Another column next the name could
> have a link like "(Log)" to get that information. That would be
> more explicit. I find it strange to click on the revision number
> to get a file because I typically do not think of files in terms
> of revision numbers. (The revision number probably should not have
> a link on it at all.)

In this template the revision number carries a link to the Log
page  (so the meaning of both columns are simply swapped).
Additionally (if you enable cvsgraph) the text string "(graph)" is replaced
by a small 16x16 pixel icon.

I experimented with the suggestion to add the word '(Log)' to each
filename but in the end I think that both (graph) and (log)
waste a lot of valueable horizontal screen space.

If you want to try this template out, you can simply start
	./standalone.py --gui
in the viewcvs directory and select the radio button labeled
with 'direasy.ezt'.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@322 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-15 18:12:22 +00:00
pefu
eae9e0131f Added the utc date (I missed that)
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@321 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-15 18:00:56 +00:00
pefu
28ecb8c8f4 * Took out mentioning ViewCVS 0.5 (that was long ago).
* Added proper credit to Bill Fenner (see
	http://mailman.lyra.org/pipermail/viewcvs/2001q4/000521.html
  )
* Added some info about the upcoming Release 0.8.
* rearranged a lot of text and added section headings.
* Added links to each section to the navigation panel at the left side
  of the page.
* Added link to the download page of the mxDateTime extension, which is
  required by MySQLdb.
* Added links to the tracker @ SF for feature requests
  and bug reports where appropriate.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@320 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-15 15:01:44 +00:00
pefu
0642725543 Fixed packing alignment of check buttons.
And: Now it is easy to compare the outcome of different templates:
Just choose the template you want to use and click reload in
your browser window.

This is currently only implemented for the directory view and
the Log page template.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@319 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-15 10:52:33 +00:00
pefu
ad9a3d8c07 Close SF patch #481914:
The included patch enables the Delphi colorizer
for .pas and .dpr files. Most code should be delphi or
some form of Turbo Pascal equivilent syntax if it uses
the .pas. Currently ViewCVS has this mapped to the
ANSI standard 'pascal' colorizer. I checked with
Michael van Canneyt, the maintainer of both the Pascal
and Delphi colorizers in Enscript and he recommended
this change.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@318 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-15 08:23:43 +00:00
timcera
7232487cf0 * Combined the 'Branches' and 'Tags' columns.
* Probably fixed the problem of many tags in the table.
* Minor format changes.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@317 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-15 02:10:26 +00:00
timcera
2e9bbd9be6 * Ran through a spell checker and corrected mistakes.
* Rearranged 'Prerequisites' and 'Optional' sections.
* Maybe some other minor clean-ups.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@316 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-14 15:06:31 +00:00
pefu
34b639106b lib/viewcvs.py:
1. Fix the group membership and ViewCVS believes unreadable-Bug pointed
    out by H. Wade Minter <minter@lunenburg.org> on the ViewCVS mailing
    list yesterday.
 2. Load configuration only once, even if the function main (or run_cgi)
    is called more than once.  This is needed to allow tinkering with the
    configuration on the fly without the need to edit the viewcvs.conf file.
standalone.py:
 * Several coment typos fixed and added more docstrings.
 * Add two check buttons to the GUI to allow tinkering with the two
   configuration variables 'use_cvsgraph' and 'use_enscript' without
   having to edit a viewcvs.conf file.
 * Allow testing of the cvsgraph feature directly from a CVS working
   working copy (cvsgraph needs to find cgi/cvsgraph.conf.dist in this
   case).
 * Added a new option to specify a path to the Apache icons used all over
   the templates.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@315 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-14 15:02:11 +00:00
pefu
18667d725d Open a new browser window to display help pages.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@314 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-13 22:32:29 +00:00
pefu
d01364d0bd * Changed path used to serve help pages to take CWD='lib' into account
(required for the templates)
* Implemented two suggestions from Gregs reviewing mails:
  1: The fragment comes after the URL. The above pattern would be something
     like: "href=\"%m%F%7#rev%R\""
  2: request.qmark_query is what you're looking for here
  Thanks, Greg.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@313 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-13 21:12:05 +00:00
timcera
c80469ba5e * Moved log to seperate line.
* Changed link text.
* Added 'Diffs' column.
* Removed link from revision number.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@312 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-13 20:59:35 +00:00
pefu
c8f661e626 * Fixed the links on the help pages to point to the viewcvs website @ SF.
* Finally removed the duplicated text from directory.ezt and replaced it
  with a 'Help' button at the upper right corner.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@311 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-13 20:54:07 +00:00
pefu
2376c9a7ef A tiny standalone Webserver. Not yet completed but works somehow.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@310 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-13 19:28:51 +00:00
pefu
2be790dcb3 Greg voted against providing local copies of the ViewCVS docs in a
ViewCVS installation.  So back these out from the doc directory again.
Remaining work:  Change the links within the Help pages.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@309 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-13 19:01:01 +00:00
pefu
61c8b3d41f Hendrik Ruijter wrote:
> Note quite there yet.
>
> map_rev_href = "href=\"%m%F%7&rev=%R&content-type=text/vnd.viewcvs-markup\"";
>
> in cvsgraph.conf works. The content-type is *essential*.

I also added comments describing the difference between this and the
previous configuration setting and a comment to describe the new options
passed from ViewCVS to cvsgraph.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@308 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-13 17:08:23 +00:00
pefu
341525d3e4 Bugfix. Hendrik Ruijter wrote:
> When a repository root other than the default
> one is chosen, cvsgraph does not take the
> ?cvsroot=FOO into account. e.g.
>
> <area shape="rect" href="files.default#rev1.1"...
>
> The %c = cvsroot (with trailing '/') in cvsgraph.conf
> is the path, not what viewcvs.cgi wants. Thus,
> it seems that 'request.amp_query' or something similar
> has to be sent as a command-line argument to cvsgraph
> and weeded out as a %0..%9.
>
> e.g.
> map_rev_href
> = "href=\"%m%F#rev%R\"";
> should be
> map_rev_href
> = "href=\"%m%F#rev%R?cvsroot=%6\"";
> where %6 is 'Development' or something similar.

Not exactly but similar.  Please test.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@307 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-13 12:06:46 +00:00
pefu
808cf5265c Added comment advertising Tim Ceras alternative log_table.ezt template
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@306 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-09 22:44:12 +00:00
pefu
e824f93fb5 make contributing.html also available locally
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@305 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-09 22:10:44 +00:00
pefu
9dcdc1a660 make request object visible to the directory.ezt template
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@304 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-09 22:08:42 +00:00
timcera
9da51dbec2 Added log_table.ezt to the installation.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@303 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-09 18:42:34 +00:00
timcera
a0503097c0 * Developed a solution for the hanging 'Diff' issue. Rather simple solution
really, which means that I probably overlooked something, but it works.
* Rearranged the diff links.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@302 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-09 18:06:11 +00:00
timcera
1e5d9dd974 * Added &nbsp; to the 'Branch' and 'Tag' columns in order to have them
maintain the row color if there aren't any entries.
* Colapsed the 'Branch Path' column into 'Branch'
* Added <br> and &nbps; tags where appropriate to correctly format each entry.
* Diff to branch point works correctly.
* Many misc. changes.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@301 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-09 18:01:49 +00:00
timcera
9322e4e59e * As near as I could determine, the rev2tag.has_key(branch_point)
was never true.  The entry.branch_point was never set.  Now works as
  expected in the log template files.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@300 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-09 17:59:56 +00:00
pefu
657b50834d Ouppss: swapped the logic in my last commit. Fix that.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@299 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-08 22:30:32 +00:00
pefu
09c4ee378a A quick'n'dirty fix for the DST time problem pointed out by
Jon Burt <jonb@inktomi.com> on the viewcvs mailing list today.
Jon posted a patch which I slightly modified (see my reply to
his EMail in the mail archive).


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@298 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-08 22:23:06 +00:00
pefu
af4ba05019 Started to "rape" one of Greg's latest pets: 'lib/ezt.py' ;-)
* Added much more "denglish" text to the module doc string!
  Attention: The docstring is probably not yet DOC-SIG (DPS) compatible.
  I lost track following the exciting work done there lately.
  Please correct any bad english grammar.  May be I will be able
  to improve my english from reviewing your diffs <wink>.

* Reworked the initial (one-and-only) test case so far to include
  at least one if block directive and fixed some typos.

* Added more doc strings to functions and methods.

* implemented an include directive.  My implementation might not be very
  elegant.  I wanted to optimize the case, where the include
  directive occurs within a for block, but the name of the include
  file is a string constant.  The include file is readed in only once then.

  To be able do this I wanted to recursivly call the parse_file method
  and append the returned 'program' to the 'program' assembled so far.
  To do this, I renamed 'parse' and 'parse_file' into '_parse' and
  '_parse_file' which return the program sequence instead of assigning
  to instance attribute directly.  To avoid a change in the interface,
  I added two new wrapper methods 'parse' and 'parse_file', which
  simply call these new private methods.

  The other case (include filename given as a value reference) is
  handled in a way similar to what was suggested by Greg: I added a
  _cmd_include() method which will invoke _parse() and _execute()
  each time again, if its execution is encountered.  This however
  will avoid (delay) reading the include file, if the directives
  happens to be in an if or else clause, which is not executed.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@297 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-08 18:55:53 +00:00
pefu
946e480778 Two minor changes in one commit:
1. make 'request' accessable to the template/directory.ezt
    (I want to use [request.script_name] for constructing a help page url)
 2. Fix tarball name generation, if someone requests to download a tarball
    of the whole repository.  Someone suggested the name 'cvs_root' somewhere
    and this guy also suggested to add a new config option to disable tarball
    generation in the root directory alltogether.  I thought about adding
    this config option but decided against it.  We should think of users
    as consenting adults and I wanted to keep configuration reasonable simple.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@296 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-08 17:29:11 +00:00
pefu
227fa91673 Change the default colors used for displaying colored diffs:
The new colors match the traffic light metapher:
1. red (stop) displays removed code no longer in use
2. yellow (attention) displays changed code
3. green (go) displays new added code.
Colors are always a matter of taste.
But I believe these colors make more sense and especially provide
more visual contrast than the previous colors copied from cvsweb.

I also modified some comments.

Please tell me, if you disagree.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@295 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-08 16:41:21 +00:00
pefu
c2d403b482 Informational stuff separated out into two help pages.
Might be removed from templates/directory.ezt.
Please have a look at my email to viewcvs-dev.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@294 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-08 08:12:56 +00:00
akr
248c7998c4 *** empty log message ***
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@293 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-08 03:01:14 +00:00
pefu
d867b9001f Fix non-matching table row tags in view diff.
The second of these patches was submitted by Christophe Jaillet (tititou)
as SF bug #478323, which should be closed now.  Greg?


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@292 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-05 19:36:02 +00:00
timcera
9639a02d25 * Added log_table.ezt as a draft table format for the log page. Need to add
logic for sorting similar to what is available on directory page. Need more
  testing to make sure format is scalable to complex repositories.
* Fixed small bug in log.ezt.  Stray 'Diff' string in the wrong place.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@291 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-11-03 04:17:17 +00:00
pefu
f5fa59bbd3 Improve setup debugging based on an idea of Tim Cera.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@290 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-31 09:15:42 +00:00
pefu
5367f945e7 swapped colors to increase contrast between first line of table and table header.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@289 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-29 07:53:01 +00:00
gstein
9a797cdd53 [ this was missed from a previous commit; checking it in... ]
The missing column is not dependent upon the show_subdir_lastmod flag -- we
don't need to propagate that into the template. If that branch of logic in
the template is reached, then we *know* that show_subdir_lastmod is enabled.
Just hard-code the column (for the revision).

Also a minor optimization: shifted the setting of row.graph_href rather than
setting it for every dir in file_data (note that we skip some of them).


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@288 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-29 04:35:51 +00:00
gstein
2af952b542 The missing column is not dependent upon the show_subdir_lastmod flag -- we
don't need to propagate that into the template. If that branch of logic in
the template is reached, then we *know* that show_subdir_lastmod is enabled.
Just hard-code the column (for the revision).

Also a minor optimization: shifted the setting of row.graph_href rather than
setting it for every dir in file_data (note that we skip some of them).


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@287 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-29 04:34:20 +00:00
pefu
86649f829b Improved error handling: if enscript stops due to wrong arguments
or some other reason, output the actual command before reraising the
Broken Pipe exeception.
I hope this change reduces the chance people will blame problems
with their enscript installation on ViewCVS.
For me this made it much easier to track down a small typo I commited
with revision 1.45 earlier this week:  To be consistent with the
'enscript'-patch, which I submitted to Markku Rossi <mtr@iki.fi> this
spring I replaced 'modula2' with 'modula_2'.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@286 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-28 11:08:28 +00:00
pefu
b2b26e4a65 removed some obviously resolved items and added a preface indicating,
that this file will go away and pointing to the SourceForge tracker URL.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@285 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-28 11:07:44 +00:00
pefu
ab0ac566fe Started to document this module. Docstring contains a runnable
example using Tim Peters 'doctest' module


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@284 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-27 14:42:43 +00:00
timcera
1c26250da7 Patch addresses 2 problems that occurred with certain combinations of the
use_cvsgraph and show_subdir_lastmod options:

* If use_cvsgraph and show_subdir_lastmod were both on, some subdirectory
  entries would get (graph) links.
* Under various combinations of settings, the directory entries would not
  line up in the proper columns.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@283 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-26 14:45:47 +00:00
pefu
c230a38f9d Added a warning about cvsgraph
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@282 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-25 21:53:28 +00:00
pefu
89a9e91a56 Allow testing help buttons from working copy
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@281 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-25 20:05:51 +00:00
pefu
773eb89f9b Updated according to my experiences to get the commit database up.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@280 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-25 20:02:22 +00:00
pefu
7ae3a23f5f New function to serve help pages from the ViewCVS install directory
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@279 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-25 19:52:34 +00:00
pefu
127d12fd9c Started testing, minor bugfix
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@278 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-25 17:34:07 +00:00
pefu
d206d1d80b Some new files were missing in the installer
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@277 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-25 16:17:06 +00:00
pefu
fa424931e7 Do not chop the documentation from the upcoming release 0.8
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@276 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-25 14:57:43 +00:00
gstein
bff90274b1 First pass at converting the database query stuff over to use templates. The
new template is templates/query.ezt and is the "query" config option in the
[templates] section.

This converts about half the code. The database results come after the
footer, but that will be fixed in a (near) future revision.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@275 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-25 09:21:47 +00:00
gstein
45d2dec67b Make sure that the graph_href value is always defined. Otherwise, testing
for it could throw an UnknownReference.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@274 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-25 08:52:16 +00:00
gstein
f5c4bab1d0 Add the developers who have made significant contributions to ViewCVS. These
people should be recognized properly. This also implies joint copyright, but
who needs that with the flexibile license we have? :-)

I don't have URLs for home pages for these people, nor do I know if they
want them listed... so just names for now.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@273 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-25 08:49:48 +00:00
pefu
0b033dc738 Message from Hendrik Ruijter Oct 23, 2001 02:16:47 pm +0200:
I think the lib/config.py must define cvsdb.row_limit.
  Hacked around the viewcvs.html_header problem and got an error
  in lib/cvsdb.py until I added row_limit = 1000 in viewcvs.conf.
  Works now.

I've not tested this!  I'v no MySQL setup.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@272 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-25 07:26:40 +00:00
pefu
155679c36f A note about security
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@271 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-25 06:26:45 +00:00
pefu
aaa04ac917 Two icons (16x16 and 32x32 pixels), which may be used in directory.ezt
or elsewhere to represent the link to the cvsgraph page


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@270 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-25 06:16:50 +00:00
pefu
260d00b24b logo should look like a blimb and not like a bomb. :-(
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@269 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-25 06:11:50 +00:00
pefu
91f6f38ae4 * Removed the Graph column from directory display and changed the File
column to colspan=2, if cfg.options.use_cvsgraph is enabled.  This is
  a very minimal change not solving what Greg called
     """the stupid stuff about table headers in view_directory() and
        directory.ezt needs to be cleaned up. Specifically, the whole
	"show_author" crap should just be an aspect of the template, not the
	code."""
  Although I still believe, that the /icons/small/image2.gif would look
  better, I left the text "(graph)" as represenation of the link in there.
* Added a link to the cvsgraph display to the log.ezt template.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@268 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-24 23:26:09 +00:00
pefu
57850b9d84 * To be more consistent, I replaced all occurences of <install-root> in
INSTALL with <VIEWCVS_INSTALL_DIRECTORY>.  I needed to reformat several
  paragraphs in order to keep line length below the 80 column treshold.

* Added a small note about extending enscript syntax highlighting to
  exotic programming languages.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@267 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-24 12:56:10 +00:00
pefu
f3538a0136 sorted enscript suffices alphabetically and added Modula-2.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@266 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-24 10:17:48 +00:00
pefu
89282b3125 * ATTENTION: I changed the meaning of the cvsgraph_path config option variable!
This is more consistent with the already existing enscript_path config option.

* Added a new subsection about enscript configuration to the INSTALL file
  similar to the subsection about cvsgraph.

* Updated the notes about cvsgraph to match the new situation. (make install
  in the cvsgraph source directory places itself into /usr/local/bin).

* Added a note about linking cvsgraph on Solaris.

* The value of the config option variable cvsgraph_conf needs to be an
  absolute path.  viewcvs-install now sets this path automatically
  to the viewcvs install directory.

Idea: Should viewcvs-install try to find cvsgraph and enscript binaries
on $PATH and enable the use*-config options automatically?


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@265 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-24 09:44:59 +00:00
pefu
10d0f30cfa * Replaced the os.system() calls inherited from cvsgraphwrapper.cgi and
cvsgraphmkimg.cgi with popen.popen().  Suggested by Greg Stein.

* Removed the cvsgraphwrapper.cgi and cvsgraphmkimg.cgi scripts.  They
  are no longer needed.  Suggested by Greg Stein.

* Fixed the viewcvs-install accordingly.

* Added some basic HTML files to the installer, so that they are available
  for access from an installed viewcvs locally.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@264 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-23 11:58:05 +00:00
pefu
de7c322520 * Replaced the os.system() calls inherited from cvsgraphwrapper.cgi and
cvsgraphmkimg.cgi with popen.popen().  Suggested by Greg Stein.

* Removed the cvsgraphwrapper.cgi and cvsgraphmkimg.cgi scripts.  They
  are no longer needed.  Suggested by Greg Stein.

* Fixed the viewcvs-install accordingly.

* Added some basic HTML files to the installer, so that they are available
  for access from an installed viewcvs locally.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@263 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-23 11:26:49 +00:00
pefu
fdabbe7687 Moved cvsgraph interface into viewcvs.
This solved the apache ScriptAlias and URL problem.
The cgi/cvsgraph* scripts are no longer needed.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@262 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-23 00:18:15 +00:00
pefu
289e6514da * Added a README file. Currently this simply refers the reader to the
existing file INSTALL.
* Similar to the README file of the Python distribution I added a new
  section titled "TO THE IMPATIENT" at the top of the INSTALL file.
* Changed the section about upgrading to refer to the HTML file.
* Added a note about the optional prerequisite 'cvsgraph'.
* Modified the default behaviour of viewcvs-install, so that it will
  use a fresh installation directory for each major release of ViewCVS
  and changed the INSTALL file to point this behaviour out.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@261 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-22 21:17:59 +00:00
pefu
132f7e2342 Greg pointed out to me:
"""The point is that we don't want somebody to install ViewCVS and have the
   group ML be on there. Otherwise, we will get stupid questions about the
   *contents* of the CVS repository. You should see my inbox... I get all kinds
   of questions about software. And I'm all, "what the hell? never heard of
   that. oh... you're probably looking at the software via ViewCVS. well... go
   away... go find the real admin" :-)
""".  This change should fix that problem.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@260 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-22 20:30:05 +00:00
pefu
06b3adbbbf * Fixed comment
* Synchronize two options with viewcvs.conf.dist


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@259 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-22 07:00:59 +00:00
pefu
77a8a74a05 Improved error handling.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@258 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-22 06:41:15 +00:00
gstein
621adf6b35 Remove two options which have become obsolete: input_text_size and
allow_version_select.

Document their removal and where their new counterparts are.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@257 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-21 11:27:04 +00:00
gstein
d89a788783 Complete the rest of the log template.
CHANGE: the "Long colored diff" format option is now specified with "l"
  rather than "H". This may affect persistent URLs or existing .conf files.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@256 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-21 11:06:25 +00:00
gstein
2dade9e281 Clean out some obsolete code, now that we're using a template.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@255 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-21 08:31:20 +00:00
gstein
3d81935a92 Shift the log entry generation to the log.ezt template.
This change is focused around augment_entry(), which adds a bunch of
additional information into the entry object. Basically, augment_entry()
contains the logic from print_log() (leaving out the HTML generation).

print_log() also had a bug fix made to it (and these fixes are in the new
augment_entry). Essentially, the "next main" computation was wrong in two
ways: it applied int() to each component of c_rev, but that would make the
later comparison always fail (int != string); the second probably was that
c_work was created by splitting rev, but it should have split next. With
this fix, you can now diff against "next main".

Did a few cleanups/optimizations (rev_order use in print_log, dropping a
separate "filename" variable when computing file_url, use entry.changed
rather than rev_map).


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@254 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-21 06:54:51 +00:00
gstein
9c1d4a954b Fix nested lists, do some small optimizations in _get_value(), and some code
cleanup.

It was possible to do [for one.two], but it was impossible to refer to
[one.two] or [one.two.field]. The _get_value() function was only checking
the first part of the dotted name for use in an iteration.

We now maintain a stack of iterations in progress (in scope, basically).
When we prepare a value reference, we pass that list and do an up-front
check for the use of an iteration variable. The preparation returns the
starting point of the value lookup, and the rest of the attribute names.

(before, we returned a (refname, split-ref) tuple, now we return (refname,
 start, rest-of-ref) tuple)

We also optimize _get_value() to use 'start' rather than ref[0] and 'rest'
rather than ref[1:] (i.e. skip an index and a slice).

The value-references in the code were changed to avoid tuple-unpacking and
just pass the whole reference to _get_value() (which does the unpacking as
part of its argument definition). This effectively hides most of the
internal format of a prepared value reference.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@253 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-21 03:26:28 +00:00
gstein
ff58af471c Create rev2tag and branch_points as lists rather than a string. In almost
all cases, we wanted the list. Semantically, this just makes more sense,
too: reduce to a string only when generating output.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@252 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-21 00:38:46 +00:00
gstein
9b25497fe9 Specify the template to use in the .conf file. This allows vhosts to select
different templates. (and simply provide flexibility to the user)


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@251 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-20 23:54:21 +00:00
pefu
b01b1c3b1e With Greg's permission changed the EMail addresses, so that they
point to the ViewCVS mailing lists.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@250 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-20 23:43:06 +00:00
gstein
e3a16b2481 *) Remove the logo from the config file (and config.py).
*) Update the upgrading.html document to note the logo removal. Add
   commentary about 0.8 not being released. Tweak the [text] options.

*) viewcvs.py: toss html_header() (and its logo usage). Change the exception
   handling to simply generate the header text rather than use
   html_header().


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@249 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-20 23:24:59 +00:00
gstein
457fa117d4 Add colspan attributes so the main body of the page will extend to the right
hand side (providing more area to fill).

Some minor edits here and there.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@248 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-20 23:14:03 +00:00
gstein
9e55650c62 *) download_url() now adds request.amp_query to all URLs since all callers
did that anyways (except for one spot -- noted below)

*) In download_link(), open the window initially pointing at about:blank
   rather than an *incorrect* URL (it didn't have amp_query). Using
   about:blank will be faster anyways; and the TARGET stuff in the anchor
   will direct it to the proper URL.

*) Add templates/log.ezt for the file-log page

*) Change view_log() to begin using the new template. It is now used for the
   top portion of the file. (e.g. more work to do on the entries and bottom
   portion of the page)

*) ezt.py now allows '/' in the directives (typically, for quoted stuff)
   (this regex stuff really should be tweaked to allow just about anything
   in the quoted portions)


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@247 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-20 22:51:56 +00:00
gstein
50e4520d0f Remove obsolete comments. Noted by Peter Funk.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@246 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-20 19:45:48 +00:00
gstein
1dbc6dcb57 Reversing an incorrect fix. The user is supposed to change the .ezt template
rather than use the .conf to affect the output pages.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@245 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-20 19:30:56 +00:00
pefu
c870cc4f23 A quick and little bit ugly, but working fix for SF Bug #473129.
'ezt.py', which belongs to the edna project is not changed.
May there is a cleaner solution possible, but I was unable to
figure out how.

The []-template macros available in directory.ezt should be documented
somewhere, but where?


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@244 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-20 15:57:33 +00:00
pefu
7a0fcf1e9e Added a reference to the project member page on SourceForge.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@243 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-20 12:55:20 +00:00
pefu
bf69d3d2aa Fix viewcvs-Bugs-472766. Sorry. I forgot to copy the file back into the working directory
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@242 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-19 14:05:40 +00:00
gstein
5526c78b4a fix bug #472585: bad revision links in the directory view.
The problem was that I "optimized" some link generation. The mime type for
viewing was moved from within the code to the template. However, that mime
typed keyed download_url() to construct the url a bit differently. And that
alternate link generation was flat out busted. (note that setting
allow_markup to zero in the .conf file would have create the same broken
links)

This change just constructs the URL manually since the net effect of
download_url() is a very simple URL. This is much clearer than the use of
the function (or the prior use of download_link())


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@241 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-19 08:26:03 +00:00
gstein
fe34dc73ad in a dev environment, we need a valid CONF_PATHNAME so that we can find the
templates. compute a real directory, and (temporarily) drop it into a global
for later usage.

eventually, we'll have a complete solution for referring to templates, based
on the configuration file.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@240 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-19 08:20:26 +00:00
gstein
c0859c9dd2 toss some obsolete options
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@239 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-19 07:02:16 +00:00
gstein
ce17dc8180 A minor cleanup of some interim code that was present (the array stuff was
old tech; the objects are new tech).


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@238 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-19 04:18:42 +00:00
pefu
fcfada618a * Added a simple navigation aid between the pages. Missing an
important file (like upgrading.html) should be avoided this way.
  Placed a preliminary logo into the upper left corner of each page.
* Added a new file 'contributing.html' based on an EMail from Greg Stein.
* Used the same chalk.jpg background texture for all html files to
  have a more consistent look.
  Changed the absolute path into a relative path, so that viewing the html
  in a local copy (distributed in the tarball) will be easier.
* Moved a paragraph from ../INSTALL into upgrading.html


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@237 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-18 14:59:11 +00:00
pefu
fe17a6c8c6 Placeholder until someon comes up with a real neat logo
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@236 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-18 14:44:33 +00:00
pefu
a8ace27847 Now people upgrading their viewcvs will be warned about the
necessary changes in configuration file


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@235 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-17 07:46:52 +00:00
pefu
55052f1854 Improved error diagnostics. Allow empty module
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@234 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-16 16:41:19 +00:00
pefu
8bb281930b Added a new subsection about upgrading
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@233 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-16 15:52:23 +00:00
timcera
e31eb7859e Removed the ',v' from the title and alt name.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@232 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-16 14:59:49 +00:00
timcera
47b652e457 Needed to figure out the cgi-bin path in case it was in an unusual location.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@231 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-16 14:48:25 +00:00
timcera
44b8620adf Patch from Matthieu Bouthors, '[ #463588 ] (Download) / (View) Correction'
https://sourceforge.net/tracker/index.php?func=detail&aid=463588&group_id=18760&atid=318760
Basically, correctly labeling the links, and making sure the mime-type is correct.
Probably affects all binaries in the repository.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@230 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-16 12:34:04 +00:00
timcera
3db5f61f5e Shouldn't hard code path to cgi-bin.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@229 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-16 12:25:06 +00:00
timcera
ca3e52b05b This is the 'fix' for the problem that occurs when you have both use_cvsgraph
and show_subdir_lastmod.  Viewcvs was not putting data from directory entries
in the correct columns.  Need to decide as a group how to handle a directory
with only subdirectories because currently has blank 'Rev' and 'Graph' columns.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@228 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-16 12:24:08 +00:00
pefu
67fe0ffd7c Untabified.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@227 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-16 09:05:16 +00:00
pefu
58f2c57c33 If the viewcvs.conf file contains in invalid setting of 'default_root',
viewcvs issues a very poor error message.  I've tried to improve that by
adding a simple print statement.

Ted Hobson wrote to the viewcvs users mailing list:
> Hi, I'm hoping I could get a little help here. I've installed viewcvs
[...]
> viewcvs.cgi and was returned the following error messages.
>
>       Python Exception Occurred
>
> Traceback (innermost last):
[...]
> in __init__
>     self.cvsroot = cfg.general.cvs_roots[self.cvsrep]
> KeyError: Development
>
> The lines of code it points to don't really help me at all. Could
> somebody explain what these lines are doing and what may be wrong?


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@226 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-16 08:41:10 +00:00
timcera
0268b877e5 Added imagemap feature to CvsGraph. Now can click on the image to go to that
particular revision, tag, or diff.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@225 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-12 05:17:00 +00:00
timcera
620307a64c Added installation of the ezt.py library, directory.ezt, and cvsgraphmkimg.cgi.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@224 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-12 04:51:19 +00:00
gstein
e833b962b9 Don't try to process virtual hosts if the section is not present in the
configuration file.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@223 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-10-06 00:47:18 +00:00
gstein
f9953b7d87 Remove some obsolete options. Point users at their new location.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@222 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-09-25 10:47:48 +00:00
gstein
0d62e2c0c9 Convert the rest of view_directory() over to use the directory.ezt template.
Directory pages (including the top-level root/module selection page) can now
be completely customized, without code changes.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@221 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-09-25 09:24:11 +00:00
gstein
cf456c5cfb Add double-quote to the regex to allow (quoted) constants in the [is]
directive.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@220 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-09-25 09:15:14 +00:00
timcera
e9dee7ae87 Added use_cvsgraph to default configuration. Set to '0'.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@219 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-09-24 22:39:03 +00:00
gstein
ec99cb429b Shift more output generation from the code (viewcvs.py) into the template
(directory.ezt)


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@218 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-09-24 12:02:01 +00:00
gstein
8093438d6b begin a document with information about upgrading.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@217 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-09-24 12:00:50 +00:00
gstein
a0135416b5 add a new directive: [is ref ref] or [is ref "value"]. like an "if"
statement, comparing the two values for equality. runs the first section if
they match, the else-section if they do not.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@216 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-09-24 11:56:21 +00:00
gstein
27bcce39a8 Fix the comment parsing: allow more than simply alphanums in the text. Now
it allows everything but the closing bracket.

Add some commentary for the regex since it is central to the operation of
the templating engine.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@215 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-09-24 10:59:33 +00:00
gstein
4e07d3a2ae remove some unused code (we know it wasn't called cuz "cvsrep" wasn't
defined anywhere)


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@214 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-09-24 10:14:16 +00:00
gstein
b8582e57c7 Begin introduction of templates. For now, just the top portion of the
directory page is generated via template.

* viewcvs.conf.dist, config.py: remove [text] options and defaults; those
    values are now simply text within the template.

* viewcvs.py: load the template, define information to pass to it, and
    invoke it.

* directory.ezt: template for the directory page (and top-level root
    selection and module page).

* ezt.py: templating library from the "edna" project. no changes from the
    HEAD in edna's cvs.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@213 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-09-23 12:07:32 +00:00
gstein
5684cd97ad yet another idea
(these should migrate to the SF trackers...)


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@212 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-09-22 23:13:25 +00:00
timcera
56695b3f26 Added CvsGraph support including documentation and installation.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@211 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-09-22 06:01:44 +00:00
timcera
352e8203cd Added <tt> </tt> to the side-by-side diffs. The <tt></tt> tags garantees a fixed width font.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@210 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-09-22 05:51:09 +00:00
timcera
350db5250c Added CvsGraph support including documentation and installation.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@209 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-09-22 05:38:32 +00:00
timcera
880690fc89 Minor format change. The '<p>' was ineffective at this point because
the browser doesn't expect HTML.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@208 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-09-22 01:39:06 +00:00
jpaint
a64dc83028 big update of commit database; new CGI, better database cache; no more
html templates; better intigration with viewcvs.cgi's HTML settings


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@207 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-08-21 19:24:40 +00:00
akr
d29fac8ba9 Fix typo. (overwright -> overwrite)
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@206 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-06-18 03:12:03 +00:00
akr
6145ac2213 Fix get_logs to return two dictionaries even when there are only directories.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@205 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-06-18 03:05:05 +00:00
akr
32da3ae204 Don't put a title in menu for branch tags if there is no branch.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@204 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-06-04 10:20:14 +00:00
gstein
cbadda8611 this actually takes two arguments
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@203 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-06-03 18:43:19 +00:00
gstein
451da3e26d Get dbi.py to work with the latest MySQLdb and mxDateTime.
Submitted by: Gottfried.Ganssauge@HAUFE.DE


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@202 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-06-01 21:57:15 +00:00
akr
5130a7347f Read whole data from `co' to avoid SIGPIPE even if it is not used.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@201 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-05-31 04:37:09 +00:00
akr
2a83c75317 Don't check Attic which is not exist in tarball generation.
Reported by: Koichiro Ohba <koichiro@ctc-g.co.jp>


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@200 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-05-31 02:48:42 +00:00
gstein
8d1daf1787 developing 0.8 now...
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@199 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-05-30 09:40:04 +00:00
gstein
6c6aee893f reflect release of 0.7
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@198 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-05-30 09:39:17 +00:00
gstein
02f2243cc7 *) updated for new CVS site
*) use cvs export and require a tag


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@196 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-05-30 09:19:18 +00:00
gstein
8766cf00ca rolling 0.7
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@195 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-05-30 09:15:03 +00:00
gstein
ba039d5a8f add a reference to Karl's book. delete a broken link.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@194 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-05-30 08:54:12 +00:00
gstein
007ad51b2c change web site references from www.lyra.org/viewcvs/ to the new
viewcvs.sourceforge.net/ site.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@193 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-05-30 08:49:19 +00:00
gstein
c3d12bb0c8 add a persistent URL format for reaching the HEAD revision
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@192 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-05-30 08:37:17 +00:00
gstein
d0de5e7ad0 add the two new lib/ modules.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@191 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-05-30 08:18:28 +00:00
gstein
4a00a0bb4f add an item for the next release
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@190 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-05-30 08:16:38 +00:00
gstein
abb4a468fc The single/double find stuff was stupid. single would always be <= double.
Looking for a double is simply looking at the character following the '@'.
Removing the second find also solved some cache-thrashing that occurred with
big CHUNK_SIZE values. Plus a general speedup. :-)


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@189 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-05-14 10:49:39 +00:00
gstein
cc3807f964 More speedups.
* avoid lstrip() -- it can take a while on large strings
* use loops instead of more complex ops. loops have a higher per-iteration
  cost, but a lower constant time. since N is small, it is a win.
* avoid "re" altogether; use string.digits in class Parser

(rcsparse is now over 10x faster since I started optimizing)


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@188 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-05-14 08:44:33 +00:00
gstein
3bff6b7378 More performance tweaks.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@187 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-05-14 05:53:35 +00:00
gstein
fc6d80e2fb Speed up the get() method by being smarter about regular expression use,
avoiding slicing of strings, and more fine-tuned parsing.

(some debug stuff, too; this will disappear soon; just checkpointing now)


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@186 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-05-13 02:52:26 +00:00
gstein
425ae2e8ec Speed up the log extraction by avoiding regular expressions (there are
simple equivalents which are faster).

Add a test function for profiling.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@185 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-05-13 02:50:24 +00:00
gstein
c2a29c83cd save a little test program for comparing rcsparse vs rlog output parsing
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@184 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-05-13 00:16:42 +00:00
gstein
799816c21f minor nit: fetch_log's last return value is a list, not a dict.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@183 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-05-13 00:13:12 +00:00
gstein
e3dbdb4fcc Update copyright years to include 2001.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@182 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-05-12 22:51:02 +00:00
gstein
c28afba169 removed unneeded #! marker
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@181 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-05-12 12:33:17 +00:00
gstein
d790f07d2c Apply a patch from apache.org: watch out (and recover) for files which have
"illegal" timestamps in them.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@180 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-05-12 12:26:46 +00:00
gstein
edf252fb2f Now, make the appropriate changes to get the split CGI script/library to
actually work: move imports, wrap run_cgi() around the logic in viewcvs.py,
shift around the CONF_PATHNAME stuff, etc.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@179 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-05-12 12:22:51 +00:00
gstein
6942dd6b4b Slice a big chunk out of viewcvs.cgi and move it to viewcvs.py.
Absolutely no other changes. No additions, no reformat, no nothing...


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@178 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-05-12 12:05:08 +00:00
gstein
a75f32fc3c Older versions of Python do not like lists passed to mktime(). Ensure that
we pass a tuple.

Fixes bug #421282
Submitted by: mjpieters@users.sourceforge.net


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@177 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-05-12 11:42:08 +00:00
gstein
e243a0e1ad Record that we couldn't stat the file, or that it is unreadable. This allows
us to actually tell the user about it (and do so!).

Also removed a spurious try/except in generate_tarball; get_file_data()
shouldn't be raising one any more.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@176 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-05-12 11:34:17 +00:00
gstein
755e4e74c3 In directory views (and tarballs), don't include files or dirs that we
cannot read.

Reported by: Jaime E. Villate <villate@fe.up.pt>


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@175 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-05-12 10:23:59 +00:00
gstein
a46b9dd3ba The (colored) links need to specify the diff_format=h option. Also correct a
spelling/typo and add missing right parents.

Submitted by: Chris Tillman <tillman@azstarnet.com>


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@174 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-05-12 09:25:53 +00:00
gstein
ab987dc6fa *) some updates for the move to SourceForge.
*) add information about ViewCVS' increased security


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@173 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-05-11 21:21:50 +00:00
akr
a6ac1d1892 handle FreeBSD's rlog output which has extra "----------------------------\n".
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@172 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-05-11 07:24:15 +00:00
akr
945cb91e38 (link_includes): use sticky parameters for a link.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@171 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-05-10 09:12:45 +00:00
gstein
262f3569c7 way different nowadays, so make the header reflect current reality. update
commentary, and keep appropriate recognition of Curt's work.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@170 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-05-09 11:35:54 +00:00
gstein
1b358fc88d switch from a line-oriented processing to buffer-oriented. provides
approximately 3x performance increase.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@169 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-05-09 11:32:47 +00:00
gstein
455faa1fe6 switch to a TokenStream plus the parser. provides for better isolation of
the token parsing semantics.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@168 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-05-09 10:30:12 +00:00
gstein
01facd113b commit a checkpoint, with some tools, before doing some heavy lifting
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@167 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-05-09 09:23:00 +00:00
gstein
67daa9e5e2 Extract the RCS parsing code into a separate module. It now uses a "sink"
model, sending events/info to the blame script.

(this allows the RCS parser to be used in numerous contexts by simply
 switching the Sink that is used)


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@166 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-05-08 19:34:58 +00:00
akr
03a5620947 (_re_rewrite_url): recognize fragment identifier.
http://sourceforge.net/tracker/?func=detail&atid=318760&aid=421284&group_id=18760


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@165 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-05-06 10:06:23 +00:00
akr
424521d40e (get_file_data): ignore files which causes failures on stat(2) such
as dangling symlinks.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@164 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-05-06 09:13:01 +00:00
akr
1eeb39f264 (html_time): Since very old timestamp (82 years, 6 months in this case)
causes overflow of plain integer, use long integer.

use `year' correctly for these old timestamps.

http://sourceforge.net/tracker/index.php?func=detail&aid=418428&group_id=1&atid=100001
http://sourceforge.net/tracker/index.php?func=detail&aid=421096&group_id=18760&atid=118760


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@163 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-05-05 19:52:42 +00:00
akr
7aa5c33589 (urlencode): Don't add redundant '?'.
http://sourceforge.net/tracker/?func=detail&atid=118760&aid=421281&group_id=18760


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@162 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-05-05 18:02:00 +00:00
gstein
60236803e4 This background will be needed when the website moves to SF.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@161 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-05-02 19:50:51 +00:00
gstein
a26cfff0ad fix viewcvs-browsing link on the page
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@160 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-05-02 19:27:36 +00:00
akr
f0c34a715d fixed at viewcvs.cgi:1.57.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@159 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-05-01 05:34:41 +00:00
akr
4cb1282b70 change option name for tarball generation from tarball' to allow_tar'.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@158 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-04-28 10:38:16 +00:00
akr
f34424ca20 make tarball generation configurable.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@157 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-04-28 08:50:28 +00:00
akr
bc6851ea6f separate branch and non-branch tags in tag menu.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@156 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-04-27 09:56:18 +00:00
akr
3b10a1763f tarball generation implemented.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@155 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-04-27 09:25:38 +00:00
akr
329d20633a use state instead of Attic/ to determine file existence if a tag is
specified.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@154 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-04-27 08:26:14 +00:00
akr
5251065e6d fix blame for empty deltatext especially 1.1 to 1.1.1.1.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@153 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-04-27 07:05:49 +00:00
akr
3d6f71ce00 couldn't handle multiple RCS-encoded string in single line.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@152 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-04-27 06:51:36 +00:00
akr
1a69430064 couldn't scan RCS-encoded string ends with @.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@151 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-04-27 06:44:20 +00:00
akr
050ad1f8fe add sticky info to blame link to fix problem with non-default repository.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@150 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-04-27 06:12:33 +00:00
akr
3965ce9221 ignore CVS directory in the repository which is used for fileattr.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@149 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-04-27 06:03:10 +00:00
akr
12003f27b5 branch selection fix. branchpoint entry may precede revisions on the
branch in rlog output.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@148 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-04-27 05:53:46 +00:00
gstein
b0b1b5603e Revamp the "co" output processing to give clearer errors. Be wary of
warnings that may be generated when doing the "co".


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@147 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-04-10 06:08:11 +00:00
gstein
2b8cc9d34c *) ignore a warning from rlog
*) fix the "hide_cvsroot" option flag processing


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@146 8cb11bc2-c004-0410-86c3-e597b4017df7
2001-04-10 05:36:06 +00:00
gstein
4ddb89e053 add the 0.6 references
add (missing) refs to who.html and license-1.html


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


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


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


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


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


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

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


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


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


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


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


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


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@123 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-05-17 06:42:49 +00:00
gstein
b21597d96d bunch of stuff about 0.5
additional updates, discussion, etc


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@122 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-05-12 11:07:37 +00:00
gstein
c553401843 roll 0.5
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@121 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-05-12 10:34:01 +00:00
gstein
ee8a705131 add compilation of lib/ files (running as the web server probably will not
allow a write to lib/, so we do it ahead of time)


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@120 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-05-12 10:26:11 +00:00
gstein
665d46a59d minor tweaks. additional info/notes/etc
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@119 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-05-12 10:20:56 +00:00
gstein
15d7883bb0 add 'use_enscript' option
change the default allow_annotate to true.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@118 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-05-12 10:18:17 +00:00
gstein
6ed78e918e fix the links that are generated
the "primordial" revision no longer links to the directory. the user can do
    that from the navbar at the top of the page (a bit more intuitive).


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@117 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-05-12 07:24:13 +00:00
jpaint
5463d8ff1b Updated the docs for the modified installation paths (ie, cgi/ directory
went away).  Maybe we should provide a sample .htaccess file which
instructs the webserver to refuse access to the non-cgi scripts.

Keep in mind I'm a Roxen fan, the the .htaccess files don't have all the
features they have in Apache ;)


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@116 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-05-12 03:23:33 +00:00
jpaint
934209abb9 have rlog.py use the rcs path set in the config file
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@115 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-05-12 03:19:09 +00:00
jpaint
a19338dd0a first draft of updated INSTALL instructions -- comments/spelling fixed
probably needed


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@114 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-05-12 02:57:11 +00:00
gstein
ee6c5f5da3 don't create a cgi/ directory.
update the shell-bang line at the top of the executable files with the
    correct python executable path.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@113 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-05-12 01:56:50 +00:00
jpaint
43d6d82b34 added popen.py to the installer
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@112 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-05-12 00:25:00 +00:00
jpaint
7ad684b001 let's not broadcast my passwords to the world...
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@111 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-05-12 00:24:10 +00:00
jpaint
25e4e5d18a Fixed up viewcvs-install script. It includes a few
files, excludes a few we don't use, and added a new field to the install
file list which will prompt before overwriting a file (viewcvs.conf, for
example).


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@110 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-05-11 23:55:07 +00:00
gstein
2719168ddc be more specific about what kind of markers we look for in a log output.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@109 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-05-11 22:34:46 +00:00
gstein
31272b7cea use dict.get() to simplify/speed some code.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@108 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-05-11 22:34:14 +00:00
jpaint
eb940183e3 script to create the MySQL database for the CVSdb part of ViewCVS
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@107 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-05-11 20:03:45 +00:00
gstein
33356d5a30 fix some previous-revision handling (generally failed on files with just a
single revision in them)
strip out the header/footer stuff from make_html()


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@106 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-05-11 11:15:54 +00:00
gstein
7ba05d4b43 revise some of the MIME type handling.
improve the header/footer for the annotated files


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@105 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-05-11 11:09:41 +00:00
gstein
083de4d0c7 initial testing/support for annotation using Curt's new module
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@104 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-05-11 01:13:23 +00:00
gstein
cbe1f36d8a remove unused code.
slight change to use math.log10()
fix problem with finding the end of a @-delimited token. It didn't work
    when the terminating @ was at the end of a line.
remove the CGI script stuff, but leave a direct-invocation for testing


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@103 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-05-11 01:12:33 +00:00
gstein
efa434d7fa use new popen.popen() for more security and to deal with filenames that
have quotes in them.
improve the check for "unified" format in diff output
detect and properly handle "\ No newline at end of file" during diff output


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@102 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-05-10 01:09:16 +00:00
gstein
7fc9018074 allow errors to be captured or tossed.
fix small bug with mode=='w' (use correct file for stdout/stderr)


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@101 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-05-10 00:58:44 +00:00
gstein
a7fe838521 better version of popen(), which allows us to not worry about quoting the
arguments to the target program.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@100 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-05-09 22:38:21 +00:00
gstein
a1e7108d76 small tweak to get this working right. 'token' had an empty string, so the
reference to token[-1] raised an error.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@99 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-05-09 11:48:49 +00:00
gstein
867883e525 untabify. indent changed, too.
despite the size of this diff, there were NO code changes. "cvs diff -b"
    to see that the change was whitespace only.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@98 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-05-09 11:21:30 +00:00
gstein
2f376b11e3 quicky script to build releases
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@97 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-05-09 11:00:50 +00:00
gstein
07763938a3 fix typo
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@96 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-05-09 10:30:29 +00:00
gstein
182aeaa7fa various simplification of the code. use re.sub() rather than line-by-line
replacements for the config variable stuff.
output a bit more information when unknown errors occur.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@95 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-05-09 10:29:13 +00:00
gstein
75d59eb292 add os.makedirs() compatibility
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@94 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-05-09 10:28:07 +00:00
gstein
18048c0a80 update the copyright and license information.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@93 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-05-09 08:36:29 +00:00
gstein
fb262791a1 first draft of the license and the "who" pages.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@92 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-05-09 02:27:16 +00:00
gstein
b2d9bf88a1 add virtual host capability: each set of vhosts can override the common
settings.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@91 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-05-09 02:00:56 +00:00
gstein
8e94cc5775 add more complex "forbidden" patterns (requested by Jon Stevens @ ASF)
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@90 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-05-08 23:54:35 +00:00
gstein
e8c829df09 expand the use of enscript. provide for disabling specific languages in case
the local installation of enscript does not support them.
add a footer to the bottom of all "marked up" displays
pass the "guessed" MIME type to markup_stream() so that we can properly
    detect images and display them properly.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@89 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-05-08 04:57:31 +00:00
gstein
b892974ffe deal with a diff that reports no change
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@88 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-05-07 13:16:34 +00:00
gstein
78022b0e38 from Tanaka Akira <akr@m17n.org>: add elisp colorization via the "enscript"
program.
  Greg mods: configured with allow_elisp_coloring and enscript_path. read
  and write in chunks rather than slurping in the entire source file.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@87 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-05-07 12:54:02 +00:00
gstein
17c4db41dc initial checkin from Curt.
this currently operates as a CGI, but we will be integrating it as a part
    of viewcvs.cgi; therefore, it is going into lib/ to become a module.
this file is "pure" Curt except for the license in the header (changed per
    Curt's email to the viewcvs-dev list), and some comments about it
    being maintained as part of the ViewCVS project.
    [ change will progress from this snapshot ]


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@86 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-04-28 14:01:48 +00:00
jpaint
8070b6bba1 import string -- fix bug
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@85 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-04-27 17:48:05 +00:00
jpaint
c8ebba802e * CVSdb now uses viewcvs.conf
* added licence text for lib/compat.py lib/config.py
* viewcvs-install now installs + sets all the paths and modes for
	installed files


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@84 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-04-26 22:46:47 +00:00
jpaint
5b24650755 just a little bit more work
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@83 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-04-25 22:26:59 +00:00
jpaint
8b9a143376 fixed bug where we called the old exit function and it's no longer
there!


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@82 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-04-25 20:35:13 +00:00
jpaint
741a3daefe working on quick-n-dirty installer in Python which will allow us to get
a release out before too long; I need to make a list of to-do items
so this is easy to get working...


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@81 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-04-25 18:44:33 +00:00
jpaint
1cf42799f7 fix bug in rlog parser when it unexpectedly comes to the end of the
pipe


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@79 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-04-24 22:13:41 +00:00
jpaint
fb144dfa9d okay, found the date problem: glibc2.0.7's mktime has problems, so I
re-wrote the time calculation code to side-step those bugs


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@78 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-04-24 19:53:12 +00:00
jpaint
7963916b11 syntax error...
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@77 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-04-23 22:54:45 +00:00
jpaint
f79830925a work around more time bugs in MySQLdb
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@76 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-04-23 22:50:51 +00:00
jpaint
2217f50d1c oh, please let me get this one right
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@75 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-04-23 22:24:38 +00:00
jpaint
7cd158651d okay, all the MySQLdb dataobject tick-based time handling methods are
broken in various ways -- reconstruct GMT ticks from time module's
mktime...


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@74 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-04-23 22:09:43 +00:00
jpaint
b35c4f4e9f okay, now time handling is REALLY fixed!
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@73 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-04-23 20:30:49 +00:00
jpaint
1f5eb1b433 finally fixed up my time handling; now all classes store time in
seconds-from-epoc, GMT


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@72 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-04-23 18:29:10 +00:00
gstein
0eee32c64c initial checkin. yanked from viewcvs.cgi
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@71 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-04-23 09:51:45 +00:00
jpaint
61d80c751e *doh* let's not send my passwords out as the default; added the
"cvsdb.enabled" attribute to enable/disable cvsdb support in
viewcvs


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@70 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-04-23 01:33:14 +00:00
jpaint
db44b541e0 shaved off another 0.5 second on my test RCS file, which has 4800 commits
and now parses in about 7.5 seconds on my K7-500; it was parsing at about
12.0 seconds before profiling


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@69 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-04-22 19:59:36 +00:00
jpaint
3d2559276c speed improvments to rlog.py by using match.groups() instead of match.group
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@68 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-04-22 19:05:05 +00:00
gstein
0c963cf529 add CVSdb config entries (these went into config.py, too)
update the URLs that lead to the ViewCVS web site


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@67 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-04-22 09:27:00 +00:00
gstein
50b3d40958 shift the config code to ../lib/config.py
shift the 1.5.x compatibility code to ../lib/compat.py
update the URL pointing to the ViewCVS web site
revise handle_config() for new config mgmt.
add LIBRARY_DIR and CONF_PATHNAME with notes about install-time behavior.
use the above two values to find our library and config file.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@66 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-04-22 09:24:53 +00:00
gstein
7bb0ae1b29 initial checkin. yanked out of viewcvs.cgi.
moved defaults into a set_defaults() method.
update the URLs that point to the ViewCVS web site.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@65 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-04-22 09:22:35 +00:00
gstein
01b472e9fd add these modules -- we probably want to redistribute them.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@64 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-04-22 08:56:46 +00:00
jpaint
1f7ea22511 okay, I've fixed the file/attic/",v" problem; now RLogData's filename is
always set with the idealized path name for the RCS file: this means no
",v" and no "Attic" directory.  There needs to be a flag set in RLogData
to show it is an Attic file, but it's not there yet


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@63 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-04-21 22:20:26 +00:00
jpaint
3044ae7fba test for the ",v" before stripping until I get the rlog
logic figured out


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@62 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-04-21 16:06:49 +00:00
jpaint
fafa768bea don't have RLogData remove the ",v" from the end of the file, in fact, we
may want to manually add it so we have an exact file match


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@61 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-04-21 16:01:38 +00:00
jpaint
9bc63db2b4 really, really lame queryform.cgi script just to make things work for now
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@60 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-04-21 15:37:42 +00:00
jpaint
d87876bb54 bug fixes to make logentries for removed files work
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@59 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-04-21 15:33:22 +00:00
jpaint
cd7aa6b5da moved the RLogData->CommitList converter to cvsdbapi
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@58 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-04-21 06:32:20 +00:00
jpaint
024ccc6da3 cgi/granny.cgi: CVS annotate a file, and color code it to show how old lines are;
red is newer, blue is older; contributed by Brian Lenahan (brianl@real.com)

various updates to bootstrap the sys.path madness by keeping the root path in the
file /etc/viewcvs/root; this is open to debate, but works for now

html-templates/querytemplate.html: HTML template used by query.cgi, this will change and/or
	be removed in the future as we figure out a sane/cool way to generate the HTML


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@57 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-04-21 06:23:37 +00:00
jpaint
36684bde60 administration program for CVSdb
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@56 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-04-21 00:43:46 +00:00
jpaint
d08fb441d7 added loginfo handler for CVSdb updates
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@55 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-04-21 00:43:04 +00:00
jpaint
ebe4f1cbeb adding CVSdb cgi
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@54 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-04-21 00:40:54 +00:00
jpaint
0f8b06c9e0 CVSdb library files
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@53 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-04-21 00:37:20 +00:00
gstein
f707ad1d3c more items.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@52 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-04-19 22:29:33 +00:00
gstein
186b003a76 small patch to fix the popup windows
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@51 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-04-19 07:31:29 +00:00
gstein
621d08cb9f Zachary Bean <xach@mint.net> reported this.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@50 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-03-25 02:24:24 +00:00
gstein
8288be7f6f one more feature
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@49 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-03-24 12:12:23 +00:00
gstein
95e1ab3303 updates for the 0.4 release
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@48 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-03-24 11:16:41 +00:00
60 changed files with 14276 additions and 2600 deletions

72
CHANGES Normal file
View File

@@ -0,0 +1,72 @@
Version 0.9.4 (released 17-Aug-2005)
* security fix: omit forbidden/hidden modules from query results.
Version 0.9.3 (released 17-May-2005)
* security fix: disallow bad "content-type" input [CAN-2004-1062]
* security fix: disallow bad "sortby" and "cvsroot" input [CAN-2002-0771]
* security fix: omit forbidden/hidden modules from tarballs [CAN-2004-0915]
Version 0.9.2 (released 15-Jan-2001)
* fix redirects to Attic for diffs
* fix diffs that have no changes (causing an infinite loop)
Version 0.9.1 (released 26-Dec-2001)
* fix a problem with some syntax in ndiff.py which isn't compatible
with Python 1.5.2 (causing problems at install time)
* remove a debug statement left in the code which continues to
append lines to /tmp/log
Version 0.9 (released 23-Dec-2001)
* create templates for the rest of the pages: markup pages, graphs,
annotation, and diff.
* add multiple language support and dynamic selection based on the
Accept-Language request header
* add support for key/value files to provide a way for user-defined
variables within templates
* add optional regex searching for file contents
* add new templates for the navigation header and the footer
* EZT changes:
- add formatting into print directives
- add parameters to [include] directives
- relax what can go in double quotes
- [include] directives are now relative to the current template
- throw an exception for unclosed blocks
* changes to standalone.py: add flag for regex search
* add more help pages
* change installer to optionally show diffs
* fix to log.ezt and log_table.ezt to select "Side by Side" properly
* create dir_alternate.ezt for the flipped rev/name links
* various UI tweaks for the directory pages
Version 0.8 (released 10-Dec-2001)
* add EZT templating mechanism for generating output pages
* big update of cvs commit database
- updated MySQL support
- new CGI
- better database caching
- switch from old templates to new EZT templates (and integration
of look-and-feel)
* optional usage of CVSGraph is now builtin
* standalone server (for testing) is now provided
* shifted some options from viewcvs.conf to the templates
* the help at the top of the pages has been shifted to separate help
pages, so experienced users don't have to keep seeing it
* paths in viewcvs.conf don't require trailing slashes any more
* tweak the colorizing for Pascal and Fortran files
* fix file readability problem where the user had access via the
group, but the process' group did not match that group
* some Daylight Savings Time fixes in the CVS commit database
* fix tarball generation (the file name) for the root dir
* changed default human-readable-diff colors to "stoplight" metaphor
* web site and doc revamps
* fix the mime types on the download, view, etc links
* improved error response when the cvs root is missing
* don't try to process vhosts if the config section is not present
* various bug fixes and UI tweaks

282
INSTALL
View File

@@ -1,28 +1,101 @@
CONTENTS
--------
TO THE IMPATIENT
INSTALLING VIEWCVS
UPGRADING VIEWCVS
SQL CHECKIN DATABASE
ENSCRIPT CONFIGURATION
CVSGRAPH CONFIGURATION
IF YOU HAVE PROBLEMS...
TO THE IMPATIENT
----------------
Congratulations on getting this far. :-)
Prerequisites: Python 1.5 or later
(http://www.python.org/)
RCS, Revision Control System
(http://www.cs.purdue.edu/homes/trinkle/RCS/)
read-only, physical access to a CVS repository
(See http://www.cvshome.org/ for more information)
Optional: a web server capable of running CGI programs
(for example, Apache at http://httpd.apache.org/)
GNU-diff to replace broken diff implementations
(http://www.gnu.org/software/diffutils/diffutils.html)
MySQL to create and query a commit database
(http://www.mysql.com/)
(http://sourceforge.net/projects/mysql-python)
(and Python 1.5.2 or later)
Enscript to colorize code displayed from the CVS repository
(http://people.ssh.com/mtr/genscript/)
CvsGraph for a graphical representation of the CVS revisions
(http://www.akhphd.au.dk/~bertho/cvsgraph/)
GUI Operation:
If you just want to see what your CVS repository looks like with
ViewCVS, type "./standalone.py -g -r /PATH/TO/CVS/ROOT". This
will start a tiny webserver serving at http://localhost:7467/.
Standard operation:
To start installing right away (on UNIX): type "./viewcvs-install"
in the current directory and answer the prompts. When it
finishes, edit the file viewcvs.conf in the installation directory
to tell viewcvs the paths to your CVS repositories. Next,
configure your web server to run <INSTALL>/cgi/viewcvs.cgi, as
appropriate for your web server. The section `INSTALLING VIEWCVS'
below is still recommended reading.
INSTALLING VIEWCVS
------------------
1) To get viewcvs.cgi to work, make sure that you have Python 1.5
installed and a webserver which is capable of executing cgi-scripts
(either based on the .cgi extension, or by placing the script
within a specific directory).
1) To get viewcvs.cgi to work, make sure that you have Python 1.5 or
greater installed and a webserver which is capable of executing
CGI scripts (either based on the .cgi extension, or by placing the
script within a specific directory).
You need to have RCS installed. Specifically, "rlog", "rcsdiff",
and "co". This script was tested against RedHat's rcs-5.7-10.rpm
Note, that the viewcvs.cgi-script needs to have READ-ONLY, physical
access to the repository (or a copy of it). Therefore, rsh/ssh or
pserver access doesn't work yet.
Note, that the viewcvs.cgi script needs to have READ-ONLY, physical
access to the CVS repository (or a copy of it). Therefore, rsh/ssh or
pserver access to the repository will not work.
2) Copy viewcvs.cgi to the cgi-script location of your web server.
For the more human readable diff formats you need a modern diff utility.
If you are using Linux, this is no problem. But on commercial unices
you might want to install GNU-diff to be able to use unified or
side-by-side diffs.
If Python is not located in /usr/local/bin, then you'll need to
edit the first line of viewcvs.cgi.
If you want to use cvsgraph, you have to obtain and install this
separately. See below. This was tested with cvsgraph-1.1.2.
3) Copy viewcvs.conf.dist to the same directory and RENAME it to
viewcvs.conf
For the checkin database to work, you will need MySQL >= 3.22,
and the Python DBAPI 2.0 module, MySQLdb. This was tested with
MySQLdb 0.9.1.
4) Edit viewcvs.conf for your specific configuration. In particular,
examine the following configuration options:
2) Installation is handled by the ./viewcvs-install script. Run this
script and you will be prompted for a installation root path.
The default is /usr/local/viewcvs-VERSION, where VERSION is
the version of this ViewCVS release. The installer sets the install
path in some of the files, and ViewCVS cannot be moved to a
different path after the install.
Note: while 'root' is usually required to create /usr/local/viewcvs,
ViewCVS does not have to be installed as root, nor does it run as root.
It is just as valid to place ViewCVS in a home directory, too.
Note: viewcvs-install will create directories if needed. It will
prompt before overwriting files that may have been modified (such
as viewcvs.conf), thus making it safe to install over the top of
a previous installation. It will always overwrite program files,
however.
3) Edit <VIEWCVS_INSTALLATION_DIRECTORY>/viewcvs.conf for your specific
configuration. In particular, examine the following configuration options:
cvs_roots
default_root
@@ -30,13 +103,182 @@ INSTALLING VIEWCVS
mime_types_file
There are some other options that are usually nice to change. See
viewcvs.conf for more information.
viewcvs.conf for more information. ViewCVS provides a working,
default look. However, if you want to customize the look of ViewCVS
then edit the files in <VIEWCVS_INSTALLATION_DIRECTORY>/templates.
You need knowledge about HTML to edit the templates.
5) That's it. Try it out.
4) The CGI programs are in <VIEWCVS_INSTALLATION_DIRECTORY>/cgi/. You can
symlink to this directory from somewhere in your published HTTP server
path if your webserver is configured to follow symbolic links. You can
also copy the installed <VIEWCVS_INSTALLATION_DIRECTORY>/cgi/*.cgi scripts
after the install (unlike the other files in ViewCVS, the CGI scripts can
be moved).
Warning: ViewCVS has not been tested on web servers operating on the
Win32 platform.
If you are using Apache, then the ScriptAlias directive is very
useful for pointing directly to the viwecvs.cgi script.
NOTE: for security reasons, it is not advisable to install ViewCVS
directly into your published HTTP directory tree (due to the MySQL
passwords in viewcvs.conf).
5) That's it for repository browsing. Instructions for getting the
SQL checkin database working are below.
WARNING: ViewCVS has not been tested on web servers operating on the
Win32 platform.
UPGRADING VIEWCVS
-----------------
Please read the file upgrading.html in the website subdirectory or
at <http://viewcvs.sourceforge.net/upgrading.html>.
SQL CHECKIN DATABASE
--------------------
This feature is a clone of the Mozilla Project's Bonsai database. It
catalogs every commit in the CVS repository into a SQL database. In fact,
the databases are 100% compatible.
Various queries can be performed on the database. After installing ViewCVS,
there are some additional steps required to get the database working.
1) You need MySQL >= 3.22, and the Python module MySQLdb 0.9.0 installed.
Python 1.5.2 is REQUIRED by MySQLdb, therefore to use this part of
ViewCVS you must be using Python 1.5.2. Additionally you will need the
mxDateTime extension. I've tested with version 1.3.0
2) You need to create a MySQL user who has permission to create databases.
Optionally, you can create a second user with read-only access to the
database.
3) Run the <VIEWCVS_INSTALLATION_DIRECTORY>/make-database script. It will
prompt you for your MySQL user, password, and the name of database you
want to create. The database name defaults to "ViewCVS". This script
creates the database and sets up the empty tables. If you run this on a
existing ViewCVS database, you will lose all your data!
4) Edit your <VIEWCVS_INSTALLATION_DIRECTORY>/viewcvs.conf file.
There is a [cvsdb] section. You will need to set:
host = # MySQL database server host
database_name = # the name of the database you created with
# make-database
user = # the read/write database user
passwd = # password for read/write database user
readonly_user = # the readonly database user -- it's pretty
# safe to use the read/write user here
readonly_passwd = # password for the readonly user
5) Two programs are provided for updating the checkin database,
cvsdbadmin and loginfo-handler. They serve two different purposes.
The cvsdbadmin program walks through your CVS repository and adds
every commit in every file. This is commonly used for initializing
the database from a repository which has been in use. The
loginfo-handler script is executed by the CVS server's CVSROOT/loginfo
system upon each commit. It makes real-time updates to the checkin
database as commits are made to the repository.
To build a database of all the commits in the CVS repository /home/cvs,
invoke: "./cvsdbadmin rebuild /home/cvs". If you want to update
the checkin database, invoke: "./cvsdbadmin update /home/cvs". The
update mode checks to see if a commit is already in the database,
and only adds it if it is absent.
To get real-time updates, you'll want to checkout the CVSROOT module
from your CVS repository and edit CVSROOT/loginfo. Add the line:
ALL (echo %{sVv}; cat) | <VIEWCVS_INSTALLATION_DIRECTORY>/loginfo-handler
If you have other scripts invoked by CVSROOT/loginfo, you will want
to make sure to change any running under the "DEFAULT" keyword to
"ALL" like the loginfo handler, and probably carefully read the
execution rules for CVSROOT/loginfo from the CVS manual.
6) You may want to modify the HTML template file:
<VIEWCVS_INSTALLATION_DIRECTORY>/templates/query.ezt
This is used by the query.cgi script to generate part of its HTML output.
At some point the currently hardcoded table output will also vanish.
7) You should be ready to go. Load up the query.cgi script and give
it a try.
ENSCRIPT CONFIGURATION
----------------------
Enscript is program that can colorize sourcecode of a lot of languages.
Linux distributions like for example SuSE Linux from at least 7.0
up to the recently released 7.3 already contain a precompiled and
configured enscript 1.6.2 package.
1) Download genscript from http://people.ssh.com/mtr/genscript/
2) Configure and compile per instructions with enscript.
(I 've not done this, since I'm using the precompiled package
delivered with SuSE Linux)
3) Set the 'use_enscript' option in viewcvs.conf to 1.
4) That's it!
5) If you want to colorize exotic languages, you might have to
patch 'lib/viewcvs.py' and add a new highlighting file to enscript.
I've done this for Modula-2 and submitted the file to the
enscript maintainer long ago. If interested in this patch for
enscript mailto:pefu@sourceforge.net
CVSGRAPH CONFIGURATION
----------------------
CvsGraph is a program that can display a clickable, graphical tree
of files in a CVS repository.
WARNING: Under certain circumstances (many revisions of a file
or many branches or both) cvsgraph can generate very huge images.
Especially on thin clients these images may crash the Web-Browser.
Currently there is no known way to avoid this behavior of cvsgraph.
So you have been warned!
Nevertheless cvsgraph can be quite helpful on repositories with
a reasonable number of revisions and branches.
1) Install viewcvs according to instructions in 'INSTALLING
VIEWCVS' section above. The installation directory is where
the 'viewcvs-install' script copied and configured the viewcvs
programs.
2) Download CvsGraph from http://www.akhphd.au.dk/~bertho/cvsgraph/
3) Configure and compile per instructions with CvsGraph. I had
problems with 'configure' finding the gd library. Had to create
a link from libgd.so to libgd.do.4.0.0. On Solaris you might
want to edit the link command line and add the option -R if
you have you libraries at non-standard location.
4) Place the 'cvsgraph' executable into a directory readable by the
userid running the web server. (default is '/usr/local/bin' if
you simply type 'make install' in the cvsgraph directory).
5) Check the setting of the 'cvsgraph_path' option in viewcvs.conf:
/usr/local/bin/ is most often NOT contained in $PATH of the
webserver process (e.g. Apache), so you will have to edit this.
Set the 'use_cvsgraph' option in viewcvs.conf to 1.
6) That's it!
7) There is a file <VIEWCVS_INSTALLATION_DIRECTORY>/cvsgraph.conf that
you may want to edit if desired to set color and font characteristics.
See the cvsgraph.conf documentation. No edits are required in
cvsgraph.conf for operation with viewcvs.
IF YOU HAVE PROBLEMS ...
@@ -69,13 +311,13 @@ If you've trouble to make viewcvs.cgi work:
CVS-Repository. The CGI-script often runs as the user 'nobody'
or 'httpd' ..
o does viewcvs find your RCS utililties? (edit rcs_path)
o does viewcvs find your RCS utilities? (edit rcs_path)
=== If something else happens or you can't get it to work:
o check the ViewCVS home page:
http://www.lyra.org/greg/python/viewcvs/
http://viewcvs.sourceforge.net/
o review the ViewCVS mailing list archive to see if somebody else had
the same problem, and it was solved:

3
README Normal file
View File

@@ -0,0 +1,3 @@
ViewCVS -- Viewing the content of CVS repositories with a Webbrowser.
Please read the file INSTALL for more information.

40
TODO
View File

@@ -1,3 +1,13 @@
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
@@ -14,14 +24,30 @@ TODO ITEMS
*) 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 viewcvs-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
----------
*) from Harri Pasanen, Feb 7: when hideattic is set, files added in a
branch are not visible.
*) 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.
*) from Dieter Deyke, Jan 12: if the CVS revisions differ by just a
keyword, then the diff output chokes.
*) no scroll bar in Netscape browser when you click "as text" on an
HTML document.
*) With old repositories containing many branches, tags or thousands
or revisions, the cvsgraph feature becomes unusable (see INSTALL).
ViewCVS 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.

192
cgi/cvsgraph.conf.dist Normal file
View File

@@ -0,0 +1,192 @@
# CvsGraph configuration
#
# - Empty lines and whitespace are ignored.
#
# - Comments start with '#' and everything until
# end of line is ignored.
#
# - Strings are C-style strings in which characters
# may be escaped with '\' and written in octal
# and hex escapes. Note that '\' must be escaped
# if it is to be entered as a character.
#
# - Some strings are expanded with printf like
# conversions which start with '%'. Not all
# are applicable at all times, in which case they
# will expand to noting.
# %c = cvsroot (with trailing '/')
# %C = cvsroot (*without* trailing '/')
# %m = module (with trailing '/')
# %M = module (*without* trailing '/')
# %f = filename without path
# %F = filename without path and with ",v" stripped
# %p = path part of filename (with trailing '/')
# %r = number of revisions
# %b = number of branches
# %% = '%'
# %R = the revision number (e.g. '1.2.4.4')
# %P = previous revision number
# %B = the branch number (e.g. '1.2.4')
# %d = date of revision
# %a = author of revision
# %s = state of revision
# %t = current tag of branch or revision
# %0..%9 = command-line argument -0 .. -9
# ViewCVS currently uses the following two command-line arguments to
# pass URL information to cvsgraph:
# -6 request.amp_query (the query preceeded with '&')
# -7 request.qmark_query (the query preceed with '?')
#
# - Numbers may be entered as octal, decimal or
# hex as in 0117, 79 and 0x4f respectively.
#
# - Fonts are numbered 0..4 (defined as in libgd)
# 0 = tiny
# 1 = small
# 2 = medium (bold)
# 3 = large
# 4 = giant
#
# - Colors are a string like html-type colors in
# the form "#rrggbb" with parts written in hex
# rr = red (00..ff)
# gg = green (00-ff)
# bb = blue (00-ff)
#
# - There are several reserved words besides of the
# feature-keywords. These additional reserved words
# expand to numerical values:
# * false = 0
# * true = 1
# * left = 0
# * center = 1
# * right = 2
# * gif = 0
# * png = 1
# * jpeg = 2
# * tiny = 0
# * small = 1
# * medium = 2
# * large = 3
# * giant = 4
# cvsroot <string>
# The *absolute* base directory where the
# CSV/RCS repository can be found
# cvsmodule <string>
#
cvsroot = "--unused--"; # unused with ViewCVS, will be overridden
cvsmodule = ""; # unused with ViewCVS -- please leave it blank
# color_bg <color>
# The background color of the image
color_bg = "#ffffff";
# date_format <string>
# The strftime(3) format string for date and time
date_format = "%d-%b-%Y %H:%M:%S";
box_shadow = true;
tag_font = medium;
tag_color = "#007000";
rev_font = giant;
rev_color = "#000000";
rev_bgcolor = "#f0f0f0";
rev_separator = 1;
rev_minline = 15;
rev_maxline = 30;
rev_lspace = 5;
rev_rspace = 5;
rev_tspace = 3;
rev_bspace = 3;
rev_text = "%d"; # or "%d\n%a, %s" for author and state too
rev_text_font = tiny;
rev_text_color = "#500020";
# branch_font <number>
# The font of the number and tags
# branch_color <color>
# All branch element's color
# branch_[lrtb]space <number>
# Interior spacing (margin)
# branch_margin <number>
# Exterior spacing
# branch_connect <number>
# Length of the vertical connector
branch_font = medium;
branch_color = "#0000c0";
branch_bgcolor = "#ffffc0";
branch_lspace = 5;
branch_rspace = 5;
branch_tspace = 3;
branch_bspace = 3;
branch_margin = 15;
branch_connect = 8;
# title <string>
# The title string is expanded (see above for details)
# title_[xy] <number>
# Postion of title
# title_font <number>
# The font
# title_align <number>
# 0 = left
# 1 = center
# 2 = right
# title_color <color>
title = "%C: %p%F\nRevisions: %r, Branches: %b";
title_x = 10;
title_y = 5;
title_font = small;
title_align = left;
title_color = "#800000";
# Margins of the image
# Note: the title is outside the margin
margin_top = 35;
margin_bottom = 10;
margin_left = 10;
margin_right = 10;
# Image format(s)
# image_type <number|{gif,jpeg,png}>
# gif (0) = Create gif image
# png (1) = Create png image
# jpeg (2) = Create jpeg image
# Image types are available if they can be found in
# the gd library. Newer versions of gd do not have
# gif anymore. CvsGraph will automatically generate
# png images instead.
# image_quality <number>
# The quality of a jpeg image (1..100)
image_type = png;
image_quality = 75;
# HTML ImageMap generation
# map_name <string>
# The name= attribute in <map name="mapname">...</map>
# map_branch_href <string>
# map_branch_alt <string>
# map_rev_href <string>
# map_rev_alt <string>
# map_diff_href <string>
# map_diff_alt <string>
# These are the href= and alt= attributes in the <area>
# tags of html. The strings are expanded (see above).
map_name = "MyMapName";
map_branch_href = "href=\"%m%F?only_with_tag=%t%8%6\"";
map_branch_alt = "alt=\"%0 %t (%B)\"";
# You might want to experiment with the following setting:
# 1. The default setting will take you to a ViewCVS generated page displaying
# that revision of the file, if you click into a revision box:
map_rev_href = "href=\"%m%F?rev=%R&content-type=text/vnd.viewcvs-markup%6\"";
# 2. This alternative setting will take you to the anchor representing this
# revision on a ViewCVS generated Log page for that file:
# map_rev_href = "href=\"%m%F%7#rev%R\"";
#
map_rev_alt = "alt=\"%1 %t (%R)\"";
map_diff_href = "href=\"%m%F.diff?r1=%P&r2=%R%8%6\"";
map_diff_alt = "alt=\"%2 %P &lt;-&gt; %R\"";

529
cgi/granny.cgi Executable file
View File

@@ -0,0 +1,529 @@
#!/usr/bin/python
# -*- Mode: python -*-
"""
Granny.py - display CVS annotations in HTML
with lines colored by code age in days.
Original Perl version by J. Gabriel Foster (gabe@sgrail.com)
Posted to info-cvs on 02/27/1997
You can still get the original shell archive here:
http://www.geocrawler.com/archives/3/382/1997/2/0/2103824/
Perl modifications for NT by Marc Paquette (marcpa@cam.org)
Python port and CGI modifications by Brian Lenihan (brianl@real.com)
From the original granny.pl README:
--
What is Granny? Why do I care?
Granny is a tool for viewing cvs annotation information graphically. Using
Netscape, granny indicates the age of lines by color. Red lines are new,
blue lines are old. This information can be very useful in determining
what lines are 'hot'. New lines are more likely to contain bugs, and this
is an easy way to visualize that information.
Requirements:
Netscape (version 2.0 or better)
Perl5
CVS 1.9 (1.8 should work, but I have not tried it.)
Installation:
Put granny somewhere in your path. You may need to edit the
first line to point to your perl5 binary and libraries.
What to do:
Run granny just like you would 'cvs annotate' for a single file.
granny thefile.C
To find out who is to blame for that new 'feature'.
granny -U thefile.C
For all your options:
granny -h
Questions, Comments, Assertions?
send e-mail to the author: Gabe Foster (gabe@sgrail.com)
Notes:
I'm not the first person to try this sort of display, I just read about it
in a magazine somewhere and decided that cvs had all the information
I needed. To whomever first had this idea, it's a great one.
Granny is free, please use it as you see fit. I give no warranties.
As a courtesy, I ask that you tell me about any modifications you have made,
and also ask that you acknowledge my work if you use Granny in your own
software.
--
Granny.py:
granny.py [-h][-d days][-i input][-o output][-DUV] file
-h: Get this help display.
-i: Specify the input file. (Use - for stdin.)
-o: Specify the output file. (Use - for stdout.)
-d: Specify the day range for the coloring.\n
-r: Specify the cvs revision of the file.
-D: Display the date the line was last edited.
-U Display the user that last edited the line.
-V: Display the version the line was last edited.
By default, granny.py executes cvs annotate on a FILE and
runs netscape to display the graphic.
It is assumed that cvs and Netscape (for command line version) are
in your path.
If granny.py is placed in the cgi-bin directory of your Web
server, it will act as a CGI script. The working directory
defaults to /usr/tmp, but it can be overridden in the class
constructor:
A = CGIAnnotate(tempdir='/tmp')
Required fields:
root The full path to the cvs root directory.
Name The module/filename of the annotated file.
Optional fields:
rev The cvs revision number to use. (default HEAD).
Set the following fields to display extra info:
showUser Display the user that last edited the line.
showVersion Display version that the line was last edited in.
showDate Display the date the line was last edited.
http://yourserver.yourdomain.com/cgi-bin/granny.py?root=/cvsroot&Name=module/file
TODO:
Add support for determining the MIME type of files and/or a binary check.
- easily done by parsing Apache (mime.conf) or Roxen (extensions) MIME
files.
Consider adding buttons to HTML for optional display fields.
Add support for launching other browsers.
"""
import os
import sys
import string
import re
import time
import getopt
import cStringIO
import tempfile
import traceback
month_num = {
'Jan' : 1, 'Feb' : 2, 'Mar' : 3, 'Apr' : 4, 'May' : 5, 'Jun' : 6,
'Jul' : 7, 'Aug' : 8, 'Sep' : 9, 'Oct' : 10, 'Nov' : 11, 'Dec' : 12
}
class Annotate:
def __init__(self):
self.day_range = 365
self.counter = 0
self.color_table = {}
self.user = {}
self.version = {}
self.rtime = {}
self.source = {}
self.tmp = None
self.tmpfile = None
self.revision = ''
self.showUser = 0
self.showDate = 0
self.showVersion = 0
self.set_today()
def run(self):
try:
self.process_args()
self.parse_raw_annotated_file()
self.write_annotated_html_file()
if self.tmp:
self.display_annotated_html_file()
finally:
if sys.exc_info()[0] is not None:
traceback.print_exc()
self.cleanup()
def cleanup(self):
if self.tmp:
self.tmp.close()
os.unlink(self.tmpfile)
sys.exit(0)
def getoutput(self, cmd):
"""
Get stdin and stderr from cmd and
return exit status and captured output
"""
if os.name == 'nt':
# os.popen is broken on win32, but seems to work so far...
pipe = os.popen('%s 2>&1' % cmd, 'r')
else:
pipe = os.popen('{ %s ; } 2>&1' % cmd, 'r')
text = pipe.read()
sts = pipe.close()
if sts == None:
sts = 0
if text[:-1] == '\n':
text = text[-1:]
return sts, text
def set_today(self):
"""
compute the start of this day
"""
(year,mon,day,hour,min,sec,dow,doy,dst) = time.gmtime(time.time())
self.today = time.mktime((year,mon,day,0,0,0,0,0,0))
def get_today(self):
return self.today
# entify stuff which breaks HTML display
# this was lifted from some Zope Code in
# StructuredText.py
#
# XXX try it with string.replace and run it in the profiler
def html_quote(self,v,
character_entities=((re.compile('&'), '&amp;'),
(re.compile("<"), '&lt;' ),
(re.compile(">"), '&gt;' ),
(re.compile('"'), '&quot;'))):
gsub = re.sub
text=str(v)
for regexp,name in character_entities:
text=gsub(regexp,name,text)
return text
def display_annotated_html_file(self):
if os.name == 'nt':
path = '"C:\\Program Files\\Netscape\\Communicator'\
'\\Program\\Netscape"'
if os.system('%s %s' % (path, self.tmpfile)) != 0:
sys.stderr.write('%s: Unable to start Netscape' % sys.argv[0])
sys.exit(1)
else:
if os.system('netscape -remote openFile\(%s\)' %
self.tmpfile) != 0:
sys.stderr.write('%s: Trying to run netscape, please wait\n' %
sys.argv[0])
if os.system('netscape &') == 0:
for i in range(10):
time.sleep(1)
if os.system('netscape -remote openFile\(%s\)' %
self.tmpfile) == 0:
break
if i == 10:
sys.stderr.write('%s:Unable to start netscape\n' %
sys.argv[0])
else:
sys.stderr.write('%s:Unable to start netscape\n' %
sys.argv[0])
# give Netscape time to read the file
# XXX big files may raise an OSError exception on NT
# if the sleep is too short.
time.sleep(5)
def get_opts(self):
opt_dict = {}
if not len(sys.argv[1:]) > 0:
self.usage()
opts, args = getopt.getopt(sys.argv[1:], 'DUVhi:o:d:r:')
for k,v in opts:
opt_dict[k] = v
return opt_dict
def process_args(self):
opts = self.get_opts()
if opts.has_key('-r'):
self.revision = '-r%s' % opts['-r']
if opts.has_key('-h'):
self.usage(help=1)
if opts.has_key('-i'):
if opts['-i'] != '-':
self.filename = v
infile = open(filename, 'r')
sys.stdin = infile
else:
self.file = sys.stdin
else:
self.filename = sys.argv[len(sys.argv) - 1]
cmd = 'cvs annotate %s %s' % (self.revision, self.filename)
status, text = self.getoutput(cmd)
if status != 0 or text == '':
sys.stderr.write("Can't run cvs annotate on %s\n" %
self.filename)
sys.stderr.write('%s\n' % text)
sys.exit(1)
self.file = cStringIO.StringIO(text)
if opts.has_key('-o'):
if opts['-o'] != '-':
outfile = open(v, 'w')
sys.stdout = outfile
else:
# this could be done without a temp file
target = sys.argv[len(sys.argv) -1]
self.tmpfile = tempfile.mktemp()
self.tmp = open(self.tmpfile, 'w')
sys.stdout = self.tmp
if opts.has_key('-d'):
if opts['-d'] > 0:
self.day_range = opts['-d']
if opts.has_key('-D'):
self.showDate = 1
if opts.has_key('-U'):
self.showUser = 1
if opts.has_key('-V'):
self.showVersion = 1
def parse_raw_annotated_file(self):
ann = re.compile('((\d+\.?)+)\s+\((\w+)\s+(\d\d)-'\
'(\w{3})-(\d\d)\): (.*)')
text = self.file.read()
lines = string.split(text, '\n')
for line in lines:
# Parse an annotate string
m = ann.search(line)
if m:
self.version[self.counter] = m.group(1)
self.user[self.counter] = m.group(3)
oldtime = self.today - time.mktime((
int(m.group(6)),
int(month_num[m.group(5)]),
int(m.group(4)),0,0,0,0,0,0))
self.rtime[self.counter] = oldtime / 86400
self.source[self.counter] = self.html_quote(m.group(7))
else:
self.source[self.counter] = self.html_quote(line)
pass
self.counter = self.counter + 1
def write_annotated_html_file(self):
if os.environ.has_key('SCRIPT_NAME'):
print 'Status: 200 OK\r\n'
print 'Content-type: text/html\r\n\r\n'
print ('<html><head><title>%s</title></head>\n' \
'<body bgcolor="#000000">\n' \
'<font color="#FFFFFF"><H1>File %s</H1>\n' \
'<H3>Code age in days</H2>' % (self.filename, self.filename))
for i in range(self.day_range + 1):
self.color_table[i] = \
self.hsvtorgb(240.0 * i / self.day_range, 1.0, 1.0)
step = self.day_range / 40
if step < 5:
step = 1
while self.day_range/step > 40:
step = step + 1
if step >= 5:
if step != 5:
step = step + 5 - (step % 5)
while self.day_range / step > 20:
step = step + 5
for i in range(self.day_range + 1, step):
print '<font color=%s>%s ' % (self.color_table[i], i),
print '<pre><code>'
for i in range(self.counter):
if self.showUser and self.user.has_key(i):
print '%s%s ' % ('<font color=#FFFFFF>',
string.ljust(self.user[i],10)),
if self.showVersion and self.version.has_key(i):
print '%s%s ' % ('<font color=#FFFFFF>',
string.ljust(self.version[i],6)),
if self.showDate and self.rtime.has_key(i):
(year,mon,day,hour,min,sec,dow,doy,dst) = time.gmtime(
self.today - self.rtime[i] * 86400)
print '<font color=#FFFFFF>%02d/%02d/%4d ' % (mon, day, year),
if self.rtime.get(i, self.day_range) < self.day_range:
fcolor = self.color_table.get(
self.rtime[i],
self.color_table[self.day_range])
else:
fcolor = self.color_table[self.day_range]
print '<font color=%s> %s' % (fcolor, self.source[i])
print ('</code></pre>\n' \
'<font color=#FFFFFF>\n' \
'<H5>Granny original Perl version by' \
'<I>J. Gabriel Foster</I>\n' \
'<ADDRESS><A HREF=\"mailto:gabe@sgrail.com\">'\
'gabe@sgrail.com</A></ADDRESS>\n' \
'Python version by <I>Brian Lenihan</I>\n' \
'<ADDRESS><A HREF=\"mailto:brianl@real.com\">' \
'brianl@real.com</A></ADDRESS>\n' \
'</body></html>')
sys.stdout.flush()
def hsvtorgb(self,h,s,v):
"""
a veritable technicolor spew
"""
if s == 0.0:
r = v; g = v; b = v
else:
if h < 0:
h = h + 360.0
elif h >= 360.0:
h = h - 360.0
h = h / 60.0
i = int(h)
f = h - i
if s > 1.0:
s = 1.0
p = v * (1.0 - s)
q = v * (1.0 - (s * f))
t = v * (1.0 - (s * (1.0 - f)))
if i == 0: r = v; g = t; b = p
elif i == 1: r = q; g = v; b = p
elif i == 2: r = p; g = v; b = t
elif i == 3: r = p; g = q; b = v
elif i == 4: r = t; g = p; b = v
elif i == 5: r = v; g = p; b = q
return '#%02X%02X%02X' % (r * 255 + 0.5, g * 255 + 0.5, b * 255 + 0.5)
def usage(self, help=None):
sys.stderr.write('\nusage: %s %s\n\n' % (
sys.argv[0],
'[-hDUV][-d days][-i input][-o output][-r rev] FILE')
)
if help is not None:
sys.stderr.write(
'-h: Get this help display.\n' \
'-i: Specify the input file. (Use - for stdin.)\n' \
'-o: Specify the output file. (Use - for stdout.)\n' \
'-d: Specify the day range for the coloring.\n' \
'-r: Specify the cvs revision of the file.\n' \
'-D: Display the date the line was last edited.\n' \
'-U Display the user that last edited the line.\n' \
'-V: Display the version the line was last edited.\n\n' \
'By default, %s executes a cvs annotate on a FILE and\n' \
'runs netscape to display the graphical ' \
'annotation\n' % sys.argv[0]
)
sys.exit(0)
class CGIAnnotate(Annotate):
def __init__(self,tempdir='/usr/tmp'):
Annotate.__init__(self)
if os.name == 'nt':
self.tempdir = os.environ.get('TEMP') or os.environ.get('TMP')
else:
# XXX need a sanity check here
self.tempdir = tempdir
os.chdir(self.tempdir)
def process_args(self):
f = cgi.FieldStorage()
cvsroot = f['root'].value
if f.has_key('showUser'):
self.showUser = 1
if f.has_key('showDate'):
self.showDate = 1
if f.has_key('showVersion'):
self.showVersion = 1
if f.has_key('rev'):
self.revision = '-r%s' % f['rev'].value
path = f['Name'].value
module = os.path.dirname(path)
self.workingdir = 'ann.%s' % os.getpid()
self.filename = os.path.basename(path)
os.mkdir(self.workingdir)
os.chdir(os.path.join(self.tempdir, self.workingdir))
os.system('cvs -d %s co %s' % (cvsroot, path))
os.chdir(module)
cmd = 'cvs annotate %s %s' % (self.revision, self.filename)
status, text = self.getoutput(cmd)
if status != 0 or text == '':
text = "Can't run cvs annotate on %s\n" % path
self.file = cStringIO.StringIO(text)
def cleanup(self):
os.chdir(self.tempdir)
os.system('rm -rf %s' % self.workingdir)
def display_annotated_html_file(self):
pass
def usage(self):
pass
if __name__ == '__main__':
if os.environ.has_key('SCRIPT_NAME'):
import cgi
A = CGIAnnotate()
else:
A = Annotate()
A.run()

51
cgi/query.cgi Executable file
View File

@@ -0,0 +1,51 @@
#!/usr/bin/python
# -*-python-*-
#
# Copyright (C) 1999-2001 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 ViewCVS
# distribution or at http://viewcvs.sourceforge.net/license-1.html.
#
# Contact information:
# Greg Stein, PO Box 760, Palo Alto, CA, 94302
# gstein@lyra.org, http://viewcvs.sourceforge.net/
#
# -----------------------------------------------------------------------
#
# query.cgi: View CVS commit database by web browser
#
# -----------------------------------------------------------------------
#
# This is a teeny stub to launch the main ViewCVS app. It checks the load
# average, then loads the (precompiled) viewcvs.py file and runs it.
#
# -----------------------------------------------------------------------
#
#########################################################################
#
# INSTALL-TIME CONFIGURATION
#
# These values will be set during the installation process. During
# development, they will remain None.
#
LIBRARY_DIR = None
#########################################################################
#
# Adjust sys.path to include our library directory
#
import sys
if LIBRARY_DIR:
sys.path.insert(0, LIBRARY_DIR)
else:
sys.path[:0] = ['../lib'] # any other places to look?
#########################################################################
import query
query.run_cgi()

File diff suppressed because it is too large Load Diff

View File

@@ -3,7 +3,7 @@
# Configuration file for ViewCVS
#
# Information on ViewCVS is located at the following web site:
# http://www.lyra.org/greg/python/viewcvs/
# http://viewcvs.sourceforge.net/
#
#---------------------------------------------------------------------------
@@ -22,11 +22,10 @@
#
# address
# main_title
# logo
# forbidden
#
# long_intro
# repository_info
# use_enscript
# use_cvsgraph
#
# For Python source colorization:
#
@@ -36,6 +35,9 @@
#
# icons
#
# Also, review the .ezt templates in the templates/ directory to adjust them
# for your particular site.
#
#
# FORMAT INFORMATION
@@ -71,7 +73,6 @@ cvs_roots =
default_root = Development
# uncomment if the RCS binaries are not on the standard path
# NOTE: the trailing slash is required!
#rcs_path = /usr/bin/
#
@@ -84,8 +85,9 @@ default_root = Development
# For example, you can use the mime.types from apache here:
#mime_types_file = /usr/local/apache/conf/mime.types
# This address is shown in the footer. It should contain the CVS maintainer.
address = <a href="mailto:gstein@lyra.org">gstein@lyra.org</a>
# This address is shown in the footer of the generated pages.
# It must be replaced with the address of the local CVS maintainer.
address = <a href="mailto:cvs-admin@insert.your.domain.here">No CVS admin address has been configured</a>
# this title is used on the main entry page
main_title = CVS Repository
@@ -94,139 +96,158 @@ main_title = CVS Repository
# This should contain a list of modules in the repository that should not be
# displayed (by default or by explicit path specification).
#
# This configuration can be a simple list of modules, or it can get quite
# complex:
#
# *) The "!" can be used before a module to explicitly state that it
# is NOT forbidden. Whenever this form is seen, then all modules will
# be forbidden unless one of the "!" modules match.
#
# *) Shell-style "glob" expressions may be used. "*" will match any
# sequence of zero or more characters, "?" will match any single
# character, "[seq]" will match any character in seq, and "[!seq]"
# will match any character not in seq.
#
# *) Tests are performed in sequence. The first match will terminate the
# testing. This allows for more complex allow/deny patterns.
#
# Tests are case-sensitive.
#
forbidden =
# forbidden = example
# forbidden = example1, example2
# Some examples:
#
# Disallow "example" but allow all others:
# forbidden = example
#
# Disallow "example1" and "example2" but allow all others:
# forbidden = example1, example2
#
# Allow *only* "example1" and "example2":
# forbidden = !example1, !example2
#
# Forbid modules starting with "x":
# forbidden = x*
#
# Allow modules starting with "x" but no others:
# forbidden = !x*
#
# Allow "xml", forbid other modules starting with "x", and allow the rest:
# forbidden = !xml, x*, !*
#
#
# This option provides a mechanism for custom key/value pairs to be
# available to templates. These are stored in key/value files (KV files).
#
# Pathnames to the KV files are listed here, specified as absolute paths
# or relative to this configuration file. The kV files follow the same
# format as this configuration file. It may have multiple, user-defined
# sections, and user-defined options in those sections. These are all
# placed into a structure available to the templates as:
#
# kv.SECTION.OPTION
#
# Note that an option name can be dotted. For example:
#
# [my_images]
# logos.small = /images/small-logo.png
# logos.big = /images/big-logo.png
#
# Templates can use these with a directive like: [kv.my_images.logos.small]
#
# Note that sections across multiple files will be merged. If two files
# have a [my_images] section, then the options will be merged together.
# If two files have the same option name in a section, then one will
# overwrite the other (it is unspecified regarding which "wins").
#
# To further categorize the KV files, and how the values are provided to
# the templates, a KV file name may be annotated with an additional level
# of dotted naming. For example:
#
# kv_files = [asf]kv/images.conf
#
# Assuming the same section as above, the template would refer to an image
# using [kv.asf.my_images.logos.small]
#
# Lastly, it is possible to use %lang% in the filenames to specify a
# substitution of the selected language-tag.
#
kv_files =
# example:
# kv_files = kv/file1.conf, kv/file2.conf, [i18n]kv/%lang%_data.conf
#
#
# The languages available to ViewCVS. There are several i18n mechanisms
# available:
#
# 1) using key/value extension system and reading KV files based on
# the selected language
# 2) GNU gettext to substitute text in the templates
# 3) using different templates, based on the selected language
#
# ### NOTE: at the moment, the GNU gettext style is not implemented
#
# This option is a comma-separated list of language-tag values. The first
# language-tag listed is the default language, and will be used if an
# Accept-Language header is not present in the request, or none of the
# user's requested languages are available. If there are ties on the
# selection of a language, then the first to appear in the list is chosen.
#
languages = en-us
# other examples:
#
# languages = en-us, de
# languages = en-us, en-gb, de
# languages = de, fr, en-us
#
#---------------------------------------------------------------------------
[images]
#
# All images are defined with three values: URL, WIDTH, HEIGHT
#
# this logo will appear on the page
logo = /icons/apache_pb.gif, 259, 32
[templates]
#
# these icons represent a back-pointer, a directory (folder), and a file.
# they are normally available in a standard Apache distribution, along
# with larger versions if these are too small for you.
# The templates are specified relative to the configuration file. Absolute
# paths may be used, if you want to keep these elsewhere.
#
back_icon = /icons/small/back.gif, 16, 16
dir_icon = /icons/small/dir.gif, 16, 16
file_icon = /icons/small/text.gif, 16, 16
# If %lang% occurs in the pathname, then the selected language will be
# substituted.
#
# Note: the selected language is defined by the "languages" item in the
# [general] section, and based on the request's Accept-Language
# header.
#
query = templates/query.ezt
footer = templates/footer.ezt
diff = templates/diff.ezt
graph = templates/graph.ezt
annotate = templates/annotate.ezt
markup = templates/markup.ezt
directory = templates/directory.ezt
# For an alternate form, where the first column displays a revision number
# and brings you to the log view (and the filename displays the HEAD), then
# you may use this template:
# directory = templates/dir_alternate.ezt
log = templates/log.ezt
# For a log view where the revisions are displayed in a table, you may
# want to try this template:
# log = templates/log_table.ezt
#---------------------------------------------------------------------------
[colors]
[cvsdb]
# background color of log entry in markup
markup_log = #ffffff
# color of change-section headings in a diff
diff_heading = #99cccc
# color of "empty" lines
diff_empty = #cccccc
# removed lines
diff_remove = #ff9999
# changed lines
diff_change = #99ff99
# added lines
diff_add = #ccccff
# empty lines in a change block (one part smaller than the other)
diff_dark_change = #99cc99
# even/odd row colors
even_odd = #ccccee, #ffffff
# navigation header (in diff screen, file view, annotation, etc)
nav_header = #9999ee
# color of text on most pages
text = #000000
# color of standard background
background = #ffffff
# color of alternate background (diffs, file view, annotations, etc)
alt_background = #eeeeee
# table header colors (normal and the sorted-by column)
column_header_normal = #cccccc
column_header_sorted = #88ff88
# Uncomment the following line for colored borders in tables
#table_border = #999999
#---------------------------------------------------------------------------
[text]
#
# WARNING: if you continue the text onto multiple lines, then make SURE that
# you indent the continuations.
#
short_intro =
<p>
Click on a directory to enter that directory. Click on a file to display
its revision history and to get a chance to display diffs between
revisions.
</p>
long_intro =
<p>
This is a WWW interface for CVS Repositories.
You can browse the file hierarchy by picking directories
(which have slashes after them, <i>e.g.</i>, <b>src/</b>).
If you pick a file, you will see the revision history
for that file.
Selecting a revision number will download that revision of
the file. There is a link at each revision to display
diffs between that revision and the previous one, and
a form at the bottom of the page that allows you to
display diffs between arbitrary revisions.
</p>
<p>
This script
(<a href="http://www.lyra.org/greg/python/viewcvs/">ViewCVS</a>)
has been written by Greg Stein
&lt;<a href="mailto:gstein@lyra.org">gstein@lyra.org</a>&gt;
based on the
<a href="http://linux.fh-heilbronn.de/~zeller/cgi/cvsweb.cgi">cvsweb</a>
script by Henner Zeller
&lt;<a href="mailto:zeller@think.de">zeller@think.de</a>&gt;;
it is covered by the
<a href="http://www.opensource.org/licenses/bsd-license.html">BSD
Licence</a>.
If you would like to use this CGI script on your own web server and
CVS tree, see Greg's
<a href="http://www.lyra.org/greg/python/viewcvs/">ViewCVS distribution
site</a>.
Please send any suggestions, comments, etc. to
<a href="mailto:gstein@lyra.org">Greg Stein</a>.
</p>
doc_info =
<h3>CVS Documentation</h3>
<blockquote>
<p>
<a href="http://www.loria.fr/~molli/cvs/doc/cvs_toc.html">CVS
User's Guide</a><br>
<a href="http://www.arc.unm.edu/~rsahu/cvs.html">CVS Tutorial</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>
repository_info =
<!-- insert repository access instructions here -->
#host = localhost
#database_name = ViewCVS
#user =
#passwd =
#readonly_user =
#readonly_passwd =
#row_limit = 1000
#---------------------------------------------------------------------------
[options]
@@ -240,9 +261,9 @@ repository_info =
# log Sort by log message
sort_by = file
# hide_attic: Hide or show files in Attic
# 1 Hide files in Attic
# 0 Show files in Attic
# hide_attic: Hide or show the contents of the Attic subdirectory
# 1 Hide dead files inside Attic subdir
# 0 Show the files which are inside the Attic subdir
hide_attic = 1
# log_sort: Sort order for CVS logs
@@ -256,7 +277,7 @@ log_sort = date
# u Unified diff
# c Context diff
# s Side by side
# H Long human readable
# l Long human readable (more context)
diff_format = h
# hide_cvsroot: Don't show the CVSROOT directory
@@ -264,14 +285,6 @@ diff_format = h
# 0 Show CVSROOT directory
hide_cvsroot = 1
# hide_non_readable: Don't show entries which cannot be read
# 1 Hide non-readable entries
# 0 Show non-readble entries
hide_non_readable = 1
# Show author of last change
show_author = 1
# set to 1 to make lines break at spaces,
# set to 0 to make no-break lines,
# set to a positive integer to make the lines cut at that length
@@ -294,10 +307,7 @@ hr_ignore_white = 1
hr_ignore_keyword_subst = 1
# allow annotation of files.
# NOTE: this requires rw-access to the CVSROOT/history file, and rw-access to
# the subdirectory to place the lock... so you maybe don't want it
# WARNING: this is not yet implemented!!
allow_annotate = 0
allow_annotate = 1
# allow pretty-printed version of files
allow_markup = 1
@@ -307,19 +317,6 @@ allow_markup = 1
# [make sure to have gzip in the path]
allow_compress = 1
# Make use of javascript functions to skip the need for submitting a form.
# For example, this way you can select one of your CVS roots without
# pressing 'Go' (... if you have more than one CVSROOT defined)
use_java_script = 1
# open Download-Links in another window
open_extern_window = 1
# The size of this extern window; this size option needs use_java_script
# to be defined
extern_window_width = 600
extern_window_height = 440
# If you have files which automatically refers to other files
# (such as HTML) then this allows you to browse the checked
# out files as if outside CVS.
@@ -344,7 +341,6 @@ show_log_in_markup = 1
# == Configuration defaults ==
# Defaults for configuration variables that shouldn't need
# to be configured..
allow_version_select = 1
#
# If you want to use Marc-Andrew Lemburg's py2html (and Just van Rossum's
@@ -360,12 +356,105 @@ py2html_path = .
# shown in the directory view
short_log_len = 80
table_padding = 2
diff_font_face = Helvetica,Arial
diff_font_size = -1
# the width of the textinput in the request-diff-form
input_text_size = 12
# should we use 'enscript' for syntax coloring?
use_enscript = 0
#
# if the enscript program is not on the path, set this value
#
enscript_path =
# enscript_path = /usr/bin/
#
# ViewCVS has its own set of mappings from filename extensions and filenames
# to languages. If the language is not supported by enscript, then it can
# be listed here to disable the use of enscript.
#
disable_enscript_lang =
# disable_enscript_lang = perl, cpp
#
# ViewCVS can generate tarball from a repository on the fly.
#
allow_tar = 0
# allow_tar = 1
#
# Use CvsGraph. See http://www.akhphd.au.dk/~bertho/cvsgraph/ for
# documentation and download.
#
use_cvsgraph = 0
# use_cvsgraph = 1
#
# if the cvsgraph program is not on the path, set this value
#
cvsgraph_path =
# cvsgraph_path = /usr/local/bin/
#
# Location of the customized cvsgraph configuration file.
# You will need an absolute pathname here:
#
cvsgraph_conf = <VIEWCVS_INSTALL_DIRECTORY>/cvsgraph.conf
#
# Set to enable regular expression search of all files in a directory
#
# WARNING:
#
# Enabling this option can consume HUGE amounts of server time. A
# "checkout" must be performed on *each* file in a directory, and
# the result needs to be searched for a match against the regular
# expression.
#
#
# SECURITY WARNING: Denial Of Service
#
# Since a user can enter the regular expression, it is possible for
# them to enter an expression with many alternatives and a lot of
# backtracking. Executing that search over thousands of lines over
# dozens of files can easily tie up a server for a long period of
# time.
#
# This option should only be used on sites with trusted users. It is
# highly inadvisable to use this on a public site.
#
use_re_search = 0
# use_re_search = 1
#---------------------------------------------------------------------------
[vhosts]
### DOC
# vhost1 = glob1, glob2
# vhost2 = glob3, glob4
# [vhost1-section]
# option = value
# [vhost1-othersection]
# option = value
# [vhost2-section]
# option = value
#
# Here is an example:
#
# [vhosts]
# lyra = *lyra.org
#
# [lyra-general]
# forbidden = hideme
#
# [lyra-options]
# show_logs = 0
#
# Note that "lyra" is the "canonical" name for all hosts in the lyra.org
# domain. This canonical name is then used within the additional, vhost-
# specific sections to override specific values in the common sections.
#
#---------------------------------------------------------------------------

153
lib/PyFontify.py Normal file
View File

@@ -0,0 +1,153 @@
"""Module to analyze Python source code; for syntax coloring tools.
Interface:
tags = fontify(pytext, searchfrom, searchto)
The 'pytext' argument is a string containing Python source code.
The (optional) arguments 'searchfrom' and 'searchto' may contain a slice in pytext.
The returned value is a list of tuples, formatted like this:
[('keyword', 0, 6, None), ('keyword', 11, 17, None), ('comment', 23, 53, None), etc. ]
The tuple contents are always like this:
(tag, startindex, endindex, sublist)
tag is one of 'keyword', 'string', 'comment' or 'identifier'
sublist is not used, hence always None.
"""
# Based on FontText.py by Mitchell S. Chapman,
# which was modified by Zachary Roadhouse,
# then un-Tk'd by Just van Rossum.
# Many thanks for regular expression debugging & authoring are due to:
# Tim (the-incredib-ly y'rs) Peters and Cristian Tismer
# So, who owns the copyright? ;-) How about this:
# Copyright 1996-1997:
# Mitchell S. Chapman,
# Zachary Roadhouse,
# Tim Peters,
# Just van Rossum
__version__ = "0.3.1"
import string, regex
# First a little helper, since I don't like to repeat things. (Tismer speaking)
import string
def replace(where, what, with):
return string.join(string.split(where, what), with)
# This list of keywords is taken from ref/node13.html of the
# Python 1.3 HTML documentation. ("access" is intentionally omitted.)
keywordsList = [
"del", "from", "lambda", "return",
"and", "elif", "global", "not", "try",
"break", "else", "if", "or", "while",
"class", "except", "import", "pass",
"continue", "finally", "in", "print",
"def", "for", "is", "raise"]
# Build up a regular expression which will match anything
# interesting, including multi-line triple-quoted strings.
commentPat = "#.*"
pat = "q[^\q\n]*\(\\\\[\000-\377][^\q\n]*\)*q"
quotePat = replace(pat, "q", "'") + "\|" + replace(pat, 'q', '"')
# Way to go, Tim!
pat = """
qqq
[^\\q]*
\(
\( \\\\[\000-\377]
\| q
\( \\\\[\000-\377]
\| [^\\q]
\| q
\( \\\\[\000-\377]
\| [^\\q]
\)
\)
\)
[^\\q]*
\)*
qqq
"""
pat = string.join(string.split(pat), '') # get rid of whitespace
tripleQuotePat = replace(pat, "q", "'") + "\|" + replace(pat, 'q', '"')
# Build up a regular expression which matches all and only
# Python keywords. This will let us skip the uninteresting
# identifier references.
# nonKeyPat identifies characters which may legally precede
# a keyword pattern.
nonKeyPat = "\(^\|[^a-zA-Z0-9_.\"']\)"
keyPat = nonKeyPat + "\("
for keyword in keywordsList:
keyPat = keyPat + keyword + "\|"
keyPat = keyPat[:-2] + "\)" + nonKeyPat
matchPat = keyPat + "\|" + commentPat + "\|" + tripleQuotePat + "\|" + quotePat
matchRE = regex.compile(matchPat)
idKeyPat = "[ \t]*[A-Za-z_][A-Za-z_0-9.]*" # Ident w. leading whitespace.
idRE = regex.compile(idKeyPat)
def fontify(pytext, searchfrom = 0, searchto = None):
if searchto is None:
searchto = len(pytext)
# Cache a few attributes for quicker reference.
search = matchRE.search
group = matchRE.group
idSearch = idRE.search
idGroup = idRE.group
tags = []
tags_append = tags.append
commentTag = 'comment'
stringTag = 'string'
keywordTag = 'keyword'
identifierTag = 'identifier'
start = 0
end = searchfrom
while 1:
start = search(pytext, end)
if start < 0 or start >= searchto:
break # EXIT LOOP
match = group(0)
end = start + len(match)
c = match[0]
if c not in "#'\"":
# Must have matched a keyword.
if start <> searchfrom:
# there's still a redundant char before and after it, strip!
match = match[1:-1]
start = start + 1
else:
# this is the first keyword in the text.
# Only a space at the end.
match = match[:-1]
end = end - 1
tags_append((keywordTag, start, end, None))
# If this was a defining keyword, look ahead to the
# following identifier.
if match in ["def", "class"]:
start = idSearch(pytext, end)
if start == end:
match = idGroup(0)
end = start + len(match)
tags_append((identifierTag, start, end, None))
elif c == "#":
tags_append((commentTag, start, end, None))
else:
tags_append((stringTag, start, end, None))
return tags
def test(path):
f = open(path)
text = f.read()
f.close()
tags = fontify(text)
for tag, start, end, sublist in tags:
print tag, `text[start:end]`

190
lib/accept.py Normal file
View File

@@ -0,0 +1,190 @@
# -*-python-*-
#
# Copyright (C) 1999-2001 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 ViewCVS
# distribution or at http://viewcvs.sourceforge.net/license-1.html.
#
# Contact information:
# Greg Stein, PO Box 760, Palo Alto, CA, 94302
# gstein@lyra.org, http://viewcvs.sourceforge.net/
#
# -----------------------------------------------------------------------
#
# accept.py: parse/handle the various Accept headers from the client
#
# -----------------------------------------------------------------------
#
import re
import string
def language(hdr):
"Parse an Accept-Language header."
# parse the header, storing results in a _LanguageSelector object
return _parse(hdr, _LanguageSelector())
# -----------------------------------------------------------------------
_re_token = re.compile(r'\s*([^\s;,"]+|"[^"]*")+\s*')
_re_param = re.compile(r';\s*([^;,"]+|"[^"]*")+\s*')
_re_split_param = re.compile(r'([^\s=])\s*=\s*(.*)')
def _parse(hdr, result):
# quick exit for empty or not-supplied header
if not hdr:
return result
pos = 0
while pos < len(hdr):
name = _re_token.match(hdr, pos)
if not name:
raise AcceptParseError()
a = result.item_class(string.lower(name.group(1)))
pos = name.end()
while 1:
# are we looking at a parameter?
match = _re_param.match(hdr, pos)
if not match:
break
param = match.group(1)
pos = match.end()
# split up the pieces of the parameter
match = _re_split_param.match(param)
if not match:
# the "=" was probably missing
continue
pname = string.lower(match.group(1))
if pname == 'q' or pname == 'qs':
try:
a.quality = float(match.group(2))
except ValueError:
# bad float literal
pass
elif pname == 'level':
try:
a.level = float(match.group(2))
except ValueError:
# bad float literal
pass
elif pname == 'charset':
a.charset = string.lower(match.group(2))
result.append(a)
if hdr[pos:pos+1] == ',':
pos = pos + 1
return result
class _AcceptItem:
def __init__(self, name):
self.name = name
self.quality = 1.0
self.level = 0.0
self.charset = ''
def __str__(self):
s = self.name
if self.quality != 1.0:
s = '%s;q=%.3f' % (s, self.quality)
if self.level != 0.0:
s = '%s;level=%.3f' % (s, self.level)
if self.charset:
s = '%s;charset=%s' % (s, self.charset)
return s
class _LanguageRange(_AcceptItem):
def matches(self, tag):
"Match the tag against self. Returns the qvalue, or None if non-matching."
if tag == self.name:
return self.quality
# are we a prefix of the available language-tag
name = self.name + '-'
if tag[:len(name)] == name:
return self.quality
return None
class _LanguageSelector:
item_class = _LanguageRange
def __init__(self):
self.requested = [ ]
def select_from(self, avail):
"""Select one of the available choices based on the request.
Note: if there isn't a match, then the first available choice is
considered the default.
avail is a list of language-tag strings of available languages
"""
# tuples of (qvalue, language-tag)
matches = [ ]
# try matching all pairs of desired vs available, recording the
# resulting qvalues. we also need to record the longest language-range
# that matches since the most specific range "wins"
for tag in avail:
longest = 0
final = 0.0
# check this tag against the requests from the user
for want in self.requested:
qvalue = want.matches(tag)
#print 'have %s. want %s. qvalue=%s' % (tag, want.name, qvalue)
if qvalue is not None and len(want.name) > longest:
# we have a match and it is longer than any we may have had
final = qvalue
longest = len(want.name)
# a non-zero qvalue is a potential match
if final:
matches.append((final, tag))
# if we have any matches, then look at the highest qvalue
if matches:
matches.sort()
qvalue, tag = matches[-1]
if len(matches) >= 2 and matches[-2][0] == qvalue:
#print "non-deterministic choice", avail
pass
# if the qvalue is non-zero, then we have a valid match
if qvalue:
return tag
# the qvalue is zero (non-match). drop thru to return the default
# return the default language tag
return avail[0]
def append(self, item):
self.requested.append(item)
class AcceptParseError(Exception):
pass
def _test():
s = language('en')
assert s.select_from(['en']) == 'en'
assert s.select_from(['en', 'de']) == 'en'
assert s.select_from(['de', 'en']) == 'en'
s = language('fr, de;q=0.9, en-gb;q=0.7, en;q=0.6, en-gb-foo;q=0.8')
assert s.select_from(['en']) == 'en'
assert s.select_from(['en-gb-foo']) == 'en-gb-foo'
assert s.select_from(['de', 'fr']) == 'fr'
assert s.select_from(['de', 'en-gb']) == 'de'
assert s.select_from(['en-gb', 'en-gb-foo']) == 'en-gb-foo'
assert s.select_from(['en-bar']) == 'en-bar'
assert s.select_from(['en-gb-bar', 'en-gb-foo']) == 'en-gb-foo'
# non-deterministic. en-gb;q=0.7 matches both avail tags.
#assert s.select_from(['en-gb-bar', 'en-gb']) == 'en-gb'

199
lib/apache_icons.py Normal file
View File

@@ -0,0 +1,199 @@
#! /usr/bin/env python
# This file was automatically generated! DO NOT EDIT!
# Howto regenerate: see ../tools/bin2inline.py
# $Id$
# You have been warned. But if you want to edit, go ahead using your
# favorite editor :-)
## vim:ts=4:et:nowrap
# [Emacs: -*- python -*-]
_ap_icons = {
"/icons/apache_pb.gif" :
'GIF89a\003\001 \000\367\000\000\377\377\377'
'\316\316\316\245\245\245\204\204\204ssskkkZ'
'ZZ!\030\030\377B\030\3771\000\275R\020\336\255'
'\204\357\234B\377\204\000\377\316\030\377\316\000\316\316\306'
'\275\275\3061\000\377c\000\377\234\000\377\357\000\377\347'
'J\357\336{\336\326\245\316\377\000\234\357J\214\377\000'
'c\347\204\234\357Rc\377\000\030\000\000\000\000\000\000'
'\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000'
'\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000'
'\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000'
'\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000'
'\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000'
'\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000'
'\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000'
'\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000'
'\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000'
'\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000'
'\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000'
'\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000'
'\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000'
'\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000'
'\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000'
'\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000'
'\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000'
'\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000'
'\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000'
'\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000'
'\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000'
'\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000'
'\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000'
'\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000'
'\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000'
'\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000'
'\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000'
'\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000'
'\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000'
'\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000'
'\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000'
'\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000'
'\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000'
'\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000'
'\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000'
'\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000'
'\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000'
'\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000'
'\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000'
'\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000'
'\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000'
'\000\000\000\000\000\000\000\000\000\000\000\000\000!\371\004'
'\001\000\000\001\000,\000\000\000\000\003\001 \000G\010'
'\377\000\003\010\034H\260\240\301\203\010\023*\\\310\260'
'\241\303\207\020#J\234H\261\242E\206\021\016\034\030'
'0\260\300\200\001\002\002l0\260!\303\006\017(Q'
'&X\331\300A\204\201\007\376\3753\020\363_\004\231'
'\007h\376;\020 \246\001\0032\011\370\024\2603\302'
'\200\231=e\006%03\002P\002\001p\352\374W'
'\020\350L\253\004\254\336\264y\261\253\327\257`\303\026'
'|Iv\203\331\012\033*P\2400a\302?\011J'
'\343\276\225@w\002\333\265\0252P\3100!\203I'
'\277\005P\376;\231\022\001\002\001._\212]\314\270'
'\261\343\307b#Dh0\000A\203\225\011R\012N'
'\371\317\203\001\017f\375\206\366+\332d^\275\245\323'
"f\030\360\240u\353\006\017\032\314\374'\200\243\300\013"
"u'Hh\333\226\356\356\335\272%\\0xa\355"
'\332\341\022/\220^\316\274y\006\015\0065{X\330'
'A3\206\202\0300\257d\270@{\202\203\014\032\210'
'\377o\000\371\253\200\363\347\313/\214\220\236 \373\220'
'\352\343\313\237\357\360\345y\271o\347\266]\233!o'
'\337\275\031L\225\222\001\010(PSQ\002\035\305S'
'T\264\011$\300\201P\305\264\240@L)\025\222S'
'rU\025\327F\016\312\244\030} \2068_\004\010'
'\030\260Rg\240\225d\332r\024T`\327ZnY'
'\205\237\\p\375\366\033[\025\2505\300^\033\000\230'
'\300\000-\271\324P\004\272\265\025\200\005\277M\360\341'
'A\305\031G\301BN"\207\335r\303Y\260\334u'
'\011I\267\220\006\326a\347\035w_\032\024\336x"'
'\226i\346\231h\246\251\346\232l\266\351\246A\017.'
'9\320y\005(\346\200\000\015\340\331PM\002X\305'
'\221Q\006\014`\337O/\031\000\225S\006\220E\200'
'\240\024\032 \200S\360\011@\000|\002E@@V'
'2i\370\317\000\216\006\320g\242\236\032\372\346\250e'
'\032U\023P \355\367_\217*J\247RM\037n'
'\377\005U\205\222)\365\023N\001\034E\033zIM'
'\010\324\001\350=*S\242Vi\352\351\260\001\310('
"'\251\314\252\367\350O\004\024\020\000_\301\3218\227"
'\001\022\030p\227\213\250M0\022a\011\240\310\222x'
'\206!\260\254|[Q\332\354\272"\032\225\222Y\241'
'\251\325\237\213l\3015c~\276\365\266\037\275x\241'
'6\322_&\201\326\300\271\023\345;\201\005\0155\351'
'\344\302\014\033\207%B\0324\367pB\030\270j\261'
'\253\034\030\224\035f\035\200\251\035x\343\205,\362\310'
'\013<\206\247\211\232\015\206\222\212\244\261\314\234qj'
'\3355\301On\331;#P\015\030\020\333k\2559'
" \244\306ENP\020\222ENL\220\302\015'\275"
'\226\321\312\221&\345m\313e|\220\226\025m\354\335'
'\325Xc\006\362\310"\227<\252d\001HfT\003'
'\033!p\000\002)\036\340\301\000\033\014\000\332\333\360'
'\232\205\322\001q\033\345\300\000w\252{d\276F\037'
'\377\204Ap\022 <\020\322O7\304\\\337~\037'
'>\020\007\0277\216R\307^~\274Pw\222\0274'
'&\327\230\213\007\001\273\234w\356\371\347\240\207.\372'
'\350\244\227\3169\301a\203M\321\201\310\212X,A'
'\257\233.;D\246\352-)\243hk\226\200e\003'
'#T\323\261\377L*\227\260\233\022e\323\257\311*'
'\005lRV\361t\340\001/U\030\227\246\312oe'
'\000\363\263go\320\000\032\011\012@\001\005@\205Z'
'\005\004\2304RJ,\355~\030\353\300\032?\253L'
'\222\342$!\374\025j\224)\237\317\356d?\2602'
'q\024\273@\257\363\311V|\242\275\002\036DF\267'
'\252\213\223\374\223\232\317x@)\0360\314\300jB'
'A\000\302OW\327\263\236\244\234\267\2239\015\353S'
'I\311\020\354\360\023\022\343]\317\200(\014[\001\200'
'b\000\217\330L)\276\221\000\214\214\343\257\275\020\000'
'\\\036X\211\002,\203\030\325%\204=\014yTW'
'\377X\230B\024\212\215=/\351Vo^\010\303|'
'\311\320.}\351V\3344\343\300\0346\240\\z\203'
'\314\005rR\304.V*l\271\353\221_\3622\303'
'\266\334k.7z"\031\333\242\032\322\334\020\\\231'
')\014\352(R\227\302%Dix\274\000\342\012r'
'\001\013\370Qp\015\201\000\007\030\347\270\224p\000q'
'\030\350\200";\3405\205Pn%\215$\010\003&'
'I\311JZ\262\222\221\264\310d\334\366.\227\235\246'
'E0b"~jd\243\375\340H-1\243@\001'
'\344V\030\004\260\315\\\022\311\315o\000\231\020\244Q'
'\340\002\270\314\245.\011\227\020\347\370e\217\003\221\316'
' \207IL\016h\240:)\331\034A\254\346\001\310'
'92L\226\013\331\002\246I\315jZ\2231w\273'
'L\034W\026\232\264\250\34649B\245]\012\000\027'
'\031\235Q&\300\341\015o\332&\236\330\300\346g\010'
'!\022p\006B\264\335\240\316\226\273\314g\2244F'
'\363\245\301])K\232Y\010\006\016\211\310\254\031\364'
"j[\023\317%'\231I\365\214\315D+\331\014J"
'H\302\315n.\3474\375\361\313^\310\330\242\276\304'
'\314\0003\273U\\~b\000\361\300S \177K\222'
':y\223$\304\331\022\217\306\321#\037}IS\251'
'\025\204j\011\341\200"\017\2511\357\024\223\003\326|'
'\244\326\304$\315\240.@\231i\222Lm\\\351\235'
'\034\272\252G\025e\245\001\376B\030\222\250\250\000\355'
'\204\215k|\366!\334\000G\217\030\010k\000\302J'
'\326\013,\261p\274\214H\323\374\002V\262\2725\254'
'\034X\016t\010"\314\237\016\363\230\232A*J\241'
'\231\020\241~\207\250\343\241\244Q\257\351E\332\351r'
'\216\3569\354@0\240K`\372M\227\020\031\3500'
"'&Y\273ZV\257\003\201@5\035+\020\315V"
'\363 \203\015-5\013K\332\322\232\366\264\250=\010'
'@\002\004\004\000;',
"/icons/small/back.gif" :
'GIF89a\020\000\020\000\242\377\000!!!'
'111ZZZ\204\204\204\214\214\214\300\300\300\000'
'\000\000\000\000\000!\371\004\001\000\000\005\000,\000\000'
'\000\000\020\000\020\000\000\003FX\272\334\015\300I`'
'L\224\213V\213\213\320\025 t\304f\020]Qn'
'(i\266\303\240\310\312Z\215D,\010\255\022\230\236'
'\20000Z\010L\203\241\320a"*\035 \321\260'
'\267\260\235\204E\307q3\310J\010`p#\001\000'
';',
"/icons/small/dir.gif" :
'GIF89a\020\000\020\000\242\377\000\377\336\255'
'\377\334\256\300\300\300\270\240}WK;+%\035\000'
'\000\000\000\000\000!\371\004\001\000\000\002\000,\000\000'
'\000\000\020\000\020\000\000\003I(b\246\376\316\2241'
'\230\262\320\2002\253\031\001v\001$GQA\025\221'
'\254y6#\333v\360"\337_\255\335dE\350\274'
'\236\341\267\012\372t\204\001\357\370`$e\314\314P'
'\0118\026 \227\251\365\212]\014\277\234n\204\021\026'
'$\000\000;',
"/icons/small/text.gif" :
'GIF89a\020\000\020\000\242\377\000\377\377\377'
'999kkk\214\214\214\300\300\300\316\316\316\347'
'\347\347\000\000\000!\371\004\001\000\000\004\000,\000\000'
'\000\000\020\000\020\000\000\003EH\272\334\276\343\025@'
"k1\261\315J'\326\\W\024D\246LA\232\002"
'j\300xFlP\212\360\262me\330\013,\307\024'
'\336\011\327Z\011\011\276\337\354\210*\032oJ\031\200'
'I,No!\316\261$\350z\275\256E\002\000;',
}
def serve_icon(pathname, fp):
if _ap_icons.has_key(pathname):
fp.write(_ap_icons[pathname])
else:
raise OSError # icon not found

576
lib/blame.py Normal file
View File

@@ -0,0 +1,576 @@
#!/usr/local/bin/python
# -*-python-*-
#
# Copyright (C) 2000-2001 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
# the LICENSE.html file which can be found at the top level of the ViewCVS
# distribution or at http://viewcvs.sourceforge.net/license-1.html.
#
# Contact information:
# Greg Stein, PO Box 760, Palo Alto, CA, 94302
# gstein@lyra.org, http://viewcvs.sourceforge.net/
#
# -----------------------------------------------------------------------
#
# blame.py: Annotate each line of a CVS file with its author,
# revision #, date, etc.
#
# -----------------------------------------------------------------------
#
# This software is being maintained as part of the ViewCVS project.
# Information is available at:
# http://viewcvs.sourceforge.net/
#
# This file is based on the cvsblame.pl portion of the Bonsai CVS tool,
# developed by Steve Lamm for Netscape Communications Corporation. More
# information about Bonsai can be found at
# http://www.mozilla.org/bonsai.html
#
# cvsblame.pl, in turn, was based on Scott Furman's cvsblame script
#
# -----------------------------------------------------------------------
#
import string
import sys
import os
import re
import time
import math
import cgi
import rcsparse
class CVSParser(rcsparse.Sink):
# Precompiled regular expressions
trunk_rev = re.compile('^[0-9]+\\.[0-9]+$')
last_branch = re.compile('(.*)\\.[0-9]+')
is_branch = re.compile('(.*)\\.0\\.([0-9]+)')
d_command = re.compile('^d(\d+)\\s(\\d+)')
a_command = re.compile('^a(\d+)\\s(\\d+)')
SECONDS_PER_DAY = 86400
def __init__(self):
self.Reset()
def Reset(self):
self.last_revision = {}
self.prev_revision = {}
self.revision_date = {}
self.revision_author = {}
self.revision_branches = {}
self.next_delta = {}
self.prev_delta = {}
self.tag_revision = {}
self.revision_symbolic_name = {}
self.timestamp = {}
self.revision_ctime = {}
self.revision_age = {}
self.revision_log = {}
self.revision_deltatext = {}
self.revision_map = []
self.lines_added = {}
self.lines_removed = {}
# Map a tag to a numerical revision number. The tag can be a symbolic
# branch tag, a symbolic revision tag, or an ordinary numerical
# revision number.
def map_tag_to_revision(self, tag_or_revision):
try:
revision = self.tag_revision[tag_or_revision]
match = self.is_branch.match(revision)
if match:
branch = match.group(1) + '.' + match.group(2)
if self.last_revision.get(branch):
return self.last_revision[branch]
else:
return match.group(1)
else:
return revision
except:
return ''
# Construct an ordered list of ancestor revisions to the given
# revision, starting with the immediate ancestor and going back
# to the primordial revision (1.1).
#
# Note: The generated path does not traverse the tree the same way
# that the individual revision deltas do. In particular,
# the path traverses the tree "backwards" on branches.
def ancestor_revisions(self, revision):
ancestors = []
revision = self.prev_revision.get(revision)
while revision:
ancestors.append(revision)
revision = self.prev_revision.get(revision)
return ancestors
# Split deltatext specified by rev to each line.
def deltatext_split(self, rev):
lines = string.split(self.revision_deltatext[rev], '\n')
if lines[-1] == '':
del lines[-1]
return lines
# Extract the given revision from the digested RCS file.
# (Essentially the equivalent of cvs up -rXXX)
def extract_revision(self, revision):
path = []
add_lines_remaining = 0
start_line = 0
count = 0
while revision:
path.append(revision)
revision = self.prev_delta.get(revision)
path.reverse()
path = path[1:] # Get rid of head revision
text = self.deltatext_split(self.head_revision)
# Iterate, applying deltas to previous revision
for revision in path:
adjust = 0
diffs = self.deltatext_split(revision)
self.lines_added[revision] = 0
self.lines_removed[revision] = 0
lines_added_now = 0
lines_removed_now = 0
for command in diffs:
dmatch = self.d_command.match(command)
amatch = self.a_command.match(command)
if add_lines_remaining > 0:
# Insertion lines from a prior "a" command
text.insert(start_line + adjust, command)
add_lines_remaining = add_lines_remaining - 1
adjust = adjust + 1
elif dmatch:
# "d" - Delete command
start_line = string.atoi(dmatch.group(1))
count = string.atoi(dmatch.group(2))
begin = start_line + adjust - 1
del text[begin:begin + count]
adjust = adjust - count
lines_removed_now = lines_removed_now + count
elif amatch:
# "a" - Add command
start_line = string.atoi(amatch.group(1))
count = string.atoi(amatch.group(2))
add_lines_remaining = count
lines_added_now = lines_added_now + count
else:
raise RuntimeError, 'Error parsing diff commands'
self.lines_added[revision] = self.lines_added[revision] + lines_added_now
self.lines_removed[revision] = self.lines_removed[revision] + lines_removed_now
return text
def set_head_revision(self, revision):
self.head_revision = revision
def set_principal_branch(self, branch_name):
self.principal_branch = branch_name
def define_tag(self, name, revision):
# Create an associate array that maps from tag name to
# revision number and vice-versa.
self.tag_revision[name] = revision
### actually, this is a bit bogus... a rev can have multiple names
self.revision_symbolic_name[revision] = name
def set_comment(self, comment):
self.file_description = comment
def set_description(self, description):
self.rcs_file_description = description
# Construct dicts that represent the topology of the RCS tree
# and other arrays that contain info about individual revisions.
#
# The following dicts are created, keyed by revision number:
# self.revision_date -- e.g. "96.02.23.00.21.52"
# self.timestamp -- seconds since 12:00 AM, Jan 1, 1970 GMT
# self.revision_author -- e.g. "tom"
# self.revision_branches -- descendant branch revisions, separated by spaces,
# e.g. "1.21.4.1 1.21.2.6.1"
# self.prev_revision -- revision number of previous *ancestor* in RCS tree.
# Traversal of this array occurs in the direction
# of the primordial (1.1) revision.
# self.prev_delta -- revision number of previous revision which forms
# the basis for the edit commands in this revision.
# This causes the tree to be traversed towards the
# trunk when on a branch, and towards the latest trunk
# revision when on the trunk.
# self.next_delta -- revision number of next "delta". Inverts prev_delta.
#
# Also creates self.last_revision, keyed by a branch revision number, which
# indicates the latest revision on a given branch,
# e.g. self.last_revision{"1.2.8"} == 1.2.8.5
def define_revision(self, revision, timestamp, author, state,
branches, next):
self.tag_revision[revision] = revision
branch = self.last_branch.match(revision).group(1)
self.last_revision[branch] = revision
#self.revision_date[revision] = date
self.timestamp[revision] = timestamp
# Pretty print the date string
ltime = time.localtime(self.timestamp[revision])
formatted_date = time.strftime("%d %b %Y %H:%M", ltime)
self.revision_ctime[revision] = formatted_date
# Save age
self.revision_age[revision] = ((time.time() - self.timestamp[revision])
/ self.SECONDS_PER_DAY)
# save author
self.revision_author[revision] = author
# ignore the state
# process the branch information
branch_text = ''
for branch in branches:
self.prev_revision[branch] = revision
self.next_delta[revision] = branch
self.prev_delta[branch] = revision
branch_text = branch_text + branch + ''
self.revision_branches[revision] = branch_text
# process the "next revision" information
if next:
self.next_delta[revision] = next
self.prev_delta[next] = revision
is_trunk_revision = self.trunk_rev.match(revision) is not None
if is_trunk_revision:
self.prev_revision[revision] = next
else:
self.prev_revision[next] = revision
# Construct associative arrays containing info about individual revisions.
#
# The following associative arrays are created, keyed by revision number:
# revision_log -- log message
# revision_deltatext -- Either the complete text of the revision,
# in the case of the head revision, or the
# encoded delta between this revision and another.
# The delta is either with respect to the successor
# revision if this revision is on the trunk or
# relative to its immediate predecessor if this
# revision is on a branch.
def set_revision_info(self, revision, log, text):
self.revision_log[revision] = log
self.revision_deltatext[revision] = text
def parse_cvs_file(self, rcs_pathname, opt_rev = None, opt_m_timestamp = None):
# Args in: opt_rev - requested revision
# opt_m - time since modified
# Args out: revision_map
# timestamp
# revision_deltatext
# CheckHidden(rcs_pathname);
try:
rcsfile = open(rcs_pathname, 'r')
except:
raise RuntimeError, ('error: %s appeared to be under CVS control, ' +
'but the RCS file is inaccessible.') % rcs_pathname
rcsparse.Parser().parse(rcsfile, self)
rcsfile.close()
if opt_rev in [None, '', 'HEAD']:
# Explicitly specified topmost revision in tree
revision = self.head_revision
else:
# Symbolic tag or specific revision number specified.
revision = self.map_tag_to_revision(opt_rev)
if revision == '':
raise RuntimeError, 'error: -r: No such revision: ' + opt_rev
# The primordial revision is not always 1.1! Go find it.
primordial = revision
while self.prev_revision.get(primordial):
primordial = self.prev_revision[primordial]
# Don't display file at all, if -m option is specified and no
# changes have been made in the specified file.
if opt_m_timestamp and self.timestamp[revision] < opt_m_timestamp:
return ''
# Figure out how many lines were in the primordial, i.e. version 1.1,
# check-in by moving backward in time from the head revision to the
# first revision.
line_count = 0
if self.revision_deltatext.get(self.head_revision):
tmp_array = self.deltatext_split(self.head_revision)
line_count = len(tmp_array)
skip = 0
rev = self.prev_revision.get(self.head_revision)
while rev:
diffs = self.deltatext_split(rev)
for command in diffs:
dmatch = self.d_command.match(command)
amatch = self.a_command.match(command)
if skip > 0:
# Skip insertion lines from a prior "a" command
skip = skip - 1
elif dmatch:
# "d" - Delete command
start_line = string.atoi(dmatch.group(1))
count = string.atoi(dmatch.group(2))
line_count = line_count - count
elif amatch:
# "a" - Add command
start_line = string.atoi(amatch.group(1))
count = string.atoi(amatch.group(2))
skip = count;
line_count = line_count + count
else:
raise RuntimeError, 'error: illegal RCS file'
rev = self.prev_revision.get(rev)
# Now, play the delta edit commands *backwards* from the primordial
# revision forward, but rather than applying the deltas to the text of
# each revision, apply the changes to an array of revision numbers.
# This creates a "revision map" -- an array where each element
# represents a line of text in the given revision but contains only
# the revision number in which the line was introduced rather than
# the line text itself.
#
# Note: These are backward deltas for revisions on the trunk and
# forward deltas for branch revisions.
# Create initial revision map for primordial version.
self.revision_map = [primordial] * line_count
ancestors = [revision, ] + self.ancestor_revisions(revision)
ancestors = ancestors[:-1] # Remove "1.1"
last_revision = primordial
ancestors.reverse()
for revision in ancestors:
is_trunk_revision = self.trunk_rev.match(revision) is not None
if is_trunk_revision:
diffs = self.deltatext_split(last_revision)
# Revisions on the trunk specify deltas that transform a
# revision into an earlier revision, so invert the translation
# of the 'diff' commands.
for command in diffs:
if skip > 0:
skip = skip - 1
else:
dmatch = self.d_command.match(command)
amatch = self.a_command.match(command)
if dmatch:
start_line = string.atoi(dmatch.group(1))
count = string.atoi(dmatch.group(2))
temp = []
while count > 0:
temp.append(revision)
count = count - 1
self.revision_map = (self.revision_map[:start_line - 1] +
temp + self.revision_map[start_line - 1:])
elif amatch:
start_line = string.atoi(amatch.group(1))
count = string.atoi(amatch.group(2))
del self.revision_map[start_line:start_line + count]
skip = count
else:
raise RuntimeError, 'Error parsing diff commands'
else:
# Revisions on a branch are arranged backwards from those on
# the trunk. They specify deltas that transform a revision
# into a later revision.
adjust = 0
diffs = self.deltatext_split(revision)
for command in diffs:
if skip > 0:
skip = skip - 1
else:
dmatch = self.d_command.match(command)
amatch = self.a_command.match(command)
if dmatch:
start_line = string.atoi(dmatch.group(1))
count = string.atoi(dmatch.group(2))
del self.revision_map[start_line + adjust - 1:start_line + adjust - 1 + count]
adjust = adjust - count
elif amatch:
start_line = string.atoi(amatch.group(1))
count = string.atoi(amatch.group(2))
skip = count
temp = []
while count > 0:
temp.append(revision)
count = count - 1
self.revision_map = (self.revision_map[:start_line + adjust] +
temp + self.revision_map[start_line + adjust:])
adjust = adjust + skip
else:
raise RuntimeError, 'Error parsing diff commands'
last_revision = revision
return revision
re_includes = re.compile('\\#(\\s*)include(\\s*)"(.*?)"')
re_filename = re.compile('(.*[\\\\/])?(.+)')
def link_includes(text, root, rcs_path, sticky = None):
match = re_includes.match(text)
if match:
incfile = match.group(3)
for rel_path in ('', 'Attic', '..'):
trial_root = os.path.join(rcs_path, rel_path)
file = os.path.join(root, trial_root)
file = os.path.normpath(os.path.join(file, incfile + ',v'))
if os.access(file, os.F_OK):
url = os.path.join(rel_path, incfile)
if sticky:
url = url + '?' + sticky
return '#%sinclude%s"<a href="%s">%s</a>"' % \
(match.group(1), match.group(2), url, incfile)
return text
def make_html(root, rcs_path, opt_rev = None, sticky = None):
filename = os.path.join(root, rcs_path)
parser = CVSParser()
revision = parser.parse_cvs_file(filename, opt_rev)
count = len(parser.revision_map)
text = parser.extract_revision(revision)
if len(text) != count:
raise RuntimeError, 'Internal consistency error'
match = re_filename.match(rcs_path)
if not match:
raise RuntimeError, 'Unable to parse filename'
file_head = match.group(1)
file_tail = match.group(2)
open_table_tag = '<table border=0 cellpadding=0 cellspacing=0 width="100%">'
startOfRow = '<tr><td colspan=3%s><pre>'
endOfRow = '</td></tr>'
print open_table_tag + (startOfRow % '')
if count == 0:
count = 1
line_num_width = int(math.log10(count)) + 1
revision_width = 3
author_width = 5
line = 0
usedlog = {}
usedlog[revision] = 1
old_revision = 0
row_color = ''
lines_in_table = 0
inMark = 0
rev_count = 0
for revision in parser.revision_map:
thisline = text[line]
line = line + 1
usedlog[revision] = 1
line_in_table = lines_in_table + 1
# Escape HTML meta-characters
thisline = cgi.escape(thisline)
# Add a link to traverse to included files
if 1: # opt_includes
thisline = link_includes(thisline, root, file_head, sticky)
output = ''
# Highlight lines
#mark_cmd;
#if (defined($mark_cmd = $mark_line{$line}) and mark_cmd != 'end':
# output = output + endOfRow + '<tr><td bgcolor=LIGHTGREEN width="100%"><pre>'
# inMark = 1
if old_revision != revision and line != 1:
if row_color == '':
row_color = ' bgcolor="#e7e7e7"'
else:
row_color = ''
if not inMark:
if lines_in_table > 100:
output = output + endOfRow + '</table>' + open_table_tag + (startOfRow % row_color)
lines_in_table = 0
else:
output = output + endOfRow + (startOfRow % row_color)
elif lines_in_table > 200 and not inMark:
output = output + endOfRow + '</table>' + open_table_tag + (startOfRow % row_color)
lines_in_table = 0
output = output + "<a name=%d></a>" % (line, )
if 1: # opt_line_nums
output = output + ('%%%dd' % (line_num_width, )) % (line, )
if old_revision != revision or rev_count > 20:
revision_width = max(revision_width, len(revision))
if parser.prev_revision.get(revision):
fname = file_tail[:-2] # strip the ",v"
url = '%s.diff?r1=%s&amp;r2=%s' % \
(fname, parser.prev_revision[revision], revision)
if sticky:
url = url + '&' + sticky
output = output + ' <a href="%s"' % (url, )
if 0: # use_layers
output = output + " onmouseover='return log(event,\"%s\",\"%s\");'" % (
parser.prev_revision[revision], revision)
output = output + ">"
else:
output = output + " "
parser.prev_revision[revision] = ''
author = parser.revision_author[revision]
# $author =~ s/%.*$//;
author_width = max(author_width, len(author))
output = output + ('%%-%ds ' % (author_width, )) % (author, )
output = output + revision
if parser.prev_revision.get(revision):
output = output + '</a>'
output = output + (' ' * (revision_width - len(revision) + 1))
old_revision = revision
rev_count = 0
else:
output = output + ' ' + (' ' * (author_width + revision_width))
rev_count = rev_count + 1
output = output + thisline
# Close the highlighted section
#if (defined $mark_cmd and mark_cmd != 'begin'):
# chop($output)
# output = output + endOfRow + (startOfRow % row_color)
# inMark = 0
print output
print endOfRow + '</table>'
def main():
if len(sys.argv) != 3:
print 'USAGE: %s cvsroot rcs-file' % sys.argv[0]
sys.exit(1)
make_html(sys.argv[1], sys.argv[2])
if __name__ == '__main__':
main()

68
lib/compat.py Normal file
View File

@@ -0,0 +1,68 @@
# -*- Mode: python -*-
#
# Copyright (C) 2000-2001 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 ViewCVS
# distribution or at http://viewcvs.sourceforge.net/license-1.html.
#
# Contact information:
# Greg Stein, PO Box 760, Palo Alto, CA, 94302
# gstein@lyra.org, http://viewcvs.sourceforge.net/
#
# -----------------------------------------------------------------------
#
# compat.py: compatibility functions for operation across Python 1.5.x
#
# -----------------------------------------------------------------------
#
import urllib
import string
import time
import re
import os
#
# urllib.urlencode() is new to Python 1.5.2
#
try:
urlencode = urllib.urlencode
except AttributeError:
def urlencode(dict):
"Encode a dictionary as application/x-url-form-encoded."
if not dict:
return ''
quote = urllib.quote_plus
keyvalue = [ ]
for key, value in dict.items():
keyvalue.append(quote(key) + '=' + quote(str(value)))
return string.join(keyvalue, '&')
#
# time.strptime() is new to Python 1.5.2
#
if hasattr(time, 'strptime'):
def cvs_strptime(timestr):
'Parse a CVS-style date/time value.'
return time.strptime(timestr, '%Y/%m/%d %H:%M:%S')
else:
_re_rev_date = re.compile('([0-9]{4})/([0-9][0-9])/([0-9][0-9]) '
'([0-9][0-9]):([0-9][0-9]):([0-9][0-9])')
def cvs_strptime(timestr):
'Parse a CVS-style date/time value.'
matches = _re_rev_date.match(timestr).groups()
return tuple(map(int, matches)) + (0, 1, 0)
#
# os.makedirs() is new to Python 1.5.2
#
try:
makedirs = os.makedirs
except AttributeError:
def makedirs(path, mode=0777):
head, tail = os.path.split(path)
if head and tail and not os.path.exists(head):
makedirs(head, mode)
os.mkdir(path, mode)

226
lib/config.py Normal file
View File

@@ -0,0 +1,226 @@
# -*- Mode: python -*-
#
# Copyright (C) 2000-2001 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 ViewCVS
# distribution or at http://viewcvs.sourceforge.net/license-1.html.
#
# Contact information:
# Greg Stein, PO Box 760, Palo Alto, CA, 94302
# gstein@lyra.org, http://viewcvs.sourceforge.net/
#
# -----------------------------------------------------------------------
#
# config.py: configuration utilities
#
# -----------------------------------------------------------------------
#
import sys
import os
import string
import ConfigParser
import fnmatch
#########################################################################
#
# CONFIGURATION
#
# There are three forms of configuration:
#
# 1) edit the viewcvs.conf created by the viewcvs-install(er)
# 2) as (1), but delete all unchanged entries from viewcvs.conf
# 3) do not use viewcvs.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 viewcvs.conf values are a bit easier
# to work with since it is raw text, rather than python literal values.
#
#########################################################################
class Config:
_sections = ('general', 'options', 'cvsdb', 'templates')
_force_multi_value = ('cvs_roots', 'forbidden', 'disable_enscript_lang',
'languages', 'kv_files')
def __init__(self):
for section in self._sections:
setattr(self, section, _sub_config())
def load_config(self, fname, vhost=None):
this_dir = os.path.dirname(sys.argv[0])
pathname = os.path.join(this_dir, fname)
self.base = os.path.dirname(pathname)
parser = ConfigParser.ConfigParser()
parser.read(pathname)
for section in self._sections:
if parser.has_section(section):
self._process_section(parser, section, section)
if vhost and parser.has_section('vhosts'):
self._process_vhost(parser, vhost)
def load_kv_files(self, language):
kv = _sub_config()
for fname in self.general.kv_files:
if fname[0] == '[':
idx = string.index(fname, ']')
parts = string.split(fname[1:idx], '.')
fname = string.strip(fname[idx+1:])
else:
parts = [ ]
fname = string.replace(fname, '%lang%', language)
parser = ConfigParser.ConfigParser()
parser.read(os.path.join(self.base, fname))
for section in parser.sections():
for option in parser.options(section):
full_name = parts + [section]
ob = kv
for name in full_name:
try:
ob = getattr(ob, name)
except AttributeError:
c = _sub_config()
setattr(ob, name, c)
ob = c
setattr(ob, option, parser.get(section, option))
return kv
def _process_section(self, parser, section, subcfg_name):
sc = getattr(self, subcfg_name)
for opt in parser.options(section):
value = parser.get(section, opt)
if opt in self._force_multi_value:
value = map(string.strip, filter(None, string.split(value, ',')))
else:
try:
value = int(value)
except ValueError:
pass
if opt == 'cvs_roots':
roots = { }
for root in value:
name, path = map(string.strip, string.split(root, ':'))
roots[name] = path
value = roots
setattr(sc, opt, value)
def _process_vhost(self, parser, vhost):
canon_vhost = self._find_canon_vhost(parser, vhost)
if not canon_vhost:
# none of the vhost sections matched
return
cv = canon_vhost + '-'
lcv = len(cv)
for section in parser.sections():
if section[:lcv] == cv:
self._process_section(parser, section, section[lcv:])
def _find_canon_vhost(self, parser, vhost):
vhost = string.lower(vhost)
for canon_vhost in parser.options('vhosts'):
value = parser.get('vhosts', canon_vhost)
patterns = map(string.lower, map(string.strip,
filter(None, string.split(value, ','))))
for pat in patterns:
if fnmatch.fnmatchcase(vhost, pat):
return canon_vhost
return None
def set_defaults(self):
"Set some default values in the configuration."
self.general.cvs_roots = {
# user-visible-name : path
"Development" : "/home/cvsroot",
}
self.general.default_root = "Development"
self.general.rcs_path = ''
self.general.mime_types_file = ''
self.general.address = '<a href="mailto:user@insert.your.domain.here">No CVS admin address has been configured</a>'
self.general.main_title = 'CVS Repository'
self.general.forbidden = ()
self.general.kv_files = [ ]
self.general.languages = ['en-us']
self.templates.directory = 'templates/directory.ezt'
self.templates.log = 'templates/log.ezt'
self.templates.query = 'templates/query.ezt'
self.templates.footer = 'templates/footer.ezt'
self.templates.diff = 'templates/diff.ezt'
self.templates.graph = 'templates/graph.ezt'
self.templates.annotate = 'templates/annotate.ezt'
self.templates.markup = 'templates/markup.ezt'
self.cvsdb.enabled = 0
self.cvsdb.host = ''
self.cvsdb.database_name = ''
self.cvsdb.user = ''
self.cvsdb.passwd = ''
self.cvsdb.readonly_user = ''
self.cvsdb.readonly_passwd = ''
self.cvsdb.row_limit = 1000
self.options.sort_by = 'file'
self.options.hide_attic = 1
self.options.log_sort = 'date'
self.options.diff_format = 'h'
self.options.hide_cvsroot = 1
self.options.hr_breakable = 1
self.options.hr_funout = 1
self.options.hr_ignore_white = 1
self.options.hr_ignore_keyword_subst = 1
self.options.allow_annotate = 1
self.options.allow_markup = 1
self.options.allow_compress = 1
self.options.checkout_magic = 1
self.options.show_subdir_lastmod = 0
self.options.show_logs = 1
self.options.show_log_in_markup = 1
self.options.py2html_path = '.'
self.options.short_log_len = 80
self.options.diff_font_face = 'Helvetica,Arial'
self.options.diff_font_size = -1
self.options.use_enscript = 0
self.options.enscript_path = ''
self.options.disable_enscript_lang = ()
self.options.allow_tar = 0
self.options.use_cvsgraph = 0
self.options.cvsgraph_path = ''
self.options.cvsgraph_conf = "<VIEWCVS_INSTALL_DIRECTORY>/cvsgraph.conf"
self.options.use_re_search = 0
def is_forbidden(self, module):
if not module:
return 0
default = 0
for pat in self.general.forbidden:
if pat[0] == '!':
default = 1
if fnmatch.fnmatchcase(module, pat[1:]):
return 0
elif fnmatch.fnmatchcase(module, pat):
return 1
return default
class _sub_config:
pass
if not hasattr(sys, 'hexversion'):
# Python 1.5 or 1.5.1. fix the syntax for ConfigParser options.
import regex
ConfigParser.option_cre = regex.compile('^\([-A-Za-z0-9._]+\)\(:\|['
+ string.whitespace
+ ']*=\)\(.*\)$')

753
lib/cvsdb.py Normal file
View File

@@ -0,0 +1,753 @@
# -*- Mode: python -*-
#
# Copyright (C) 2000 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 ViewCVS
# distribution or at http://www.lyra.org/viewcvs/license-1.html.
#
# Contact information:
# Greg Stein, PO Box 760, Palo Alto, CA, 94302
# gstein@lyra.org, http://www.lyra.org/viewcvs/
#
# -----------------------------------------------------------------------
#
#########################################################################
#
# INSTALL-TIME CONFIGURATION
#
# These values will be set during the installation process. During
# development, they will remain None.
#
CONF_PATHNAME = None
#########################################################################
import os
import sys
import string
import time
import config
import dbi
import rlog
## load configuration file, the data is used globally here
if CONF_PATHNAME:
_cfg_pathname = CONF_PATHNAME
else:
# developer assistance: running from a CVS working copy
_cfg_pathname = os.path.join(os.path.dirname(__file__), os.pardir, 'cgi',
'viewcvs.conf')
cfg = config.Config()
cfg.set_defaults()
cfg.load_config(_cfg_pathname)
## error
error = "cvsdb error"
## cached (active) database connections
gCheckinDatabase = None
gCheckinDatabaseReadOnly = None
## CheckinDatabase provides all interfaces needed to the SQL database
## back-end; it needs to be subclassed, and have its "Connect" method
## defined to actually be complete; it should run well off of any DBI 2.0
## complient database interface
class CheckinDatabase:
def __init__(self, host, user, passwd, database):
self._host = host
self._user = user
self._passwd = passwd
self._database = database
## database lookup caches
self._get_cache = {}
self._get_id_cache = {}
self._desc_id_cache = {}
def Connect(self):
self.db = dbi.connect(
self._host, self._user, self._passwd, self._database)
def sql_get_id(self, table, column, value, auto_set):
sql = "SELECT id FROM %s WHERE %s=%%s" % (table, column)
sql_args = (value, )
cursor = self.db.cursor()
cursor.execute(sql, sql_args)
try:
(id, ) = cursor.fetchone()
except TypeError:
if not auto_set:
return None
else:
return str(int(id))
## insert the new identifier
sql = "INSERT INTO %s(%s) VALUES(%%s)" % (table, column)
sql_args = (value, )
cursor.execute(sql, sql_args)
return self.sql_get_id(table, column, value, 0)
def get_id(self, table, column, value, auto_set):
## attempt to retrieve from cache
try:
return self._get_id_cache[table][column][value]
except KeyError:
pass
id = self.sql_get_id(table, column, value, auto_set)
if id == None:
return None
## add to cache
try:
temp = self._get_id_cache[table]
except KeyError:
temp = self._get_id_cache[table] = {}
try:
temp2 = temp[column]
except KeyError:
temp2 = temp[column] = {}
temp2[value] = id
return id
def sql_get(self, table, column, id):
sql = "SELECT %s FROM %s WHERE id=%%s" % (column, table)
sql_args = (id, )
cursor = self.db.cursor()
cursor.execute(sql, sql_args)
try:
(value, ) = cursor.fetchone()
except TypeError:
return None
return value
def get(self, table, column, id):
## attempt to retrieve from cache
try:
return self._get_cache[table][column][id]
except KeyError:
pass
value = self.sql_get(table, column, id)
if value == None:
return None
## add to cache
try:
temp = self._get_cache[table]
except KeyError:
temp = self._get_cache[table] = {}
try:
temp2 = temp[column]
except KeyError:
temp2 = temp[column] = {}
temp2[id] = value
return value
def get_list(self, table, field_index):
sql = "SELECT * FROM %s" % (table)
cursor = self.db.cursor()
cursor.execute(sql)
list = []
while 1:
row = cursor.fetchone()
if row == None:
break
list.append(row[field_index])
return list
def GetBranchID(self, branch, auto_set = 1):
return self.get_id("branches", "branch", branch, auto_set)
def GetBranch(self, id):
return self.get("branches", "branch", id)
def GetDirectoryID(self, dir, auto_set = 1):
return self.get_id("dirs", "dir", dir, auto_set)
def GetDirectory(self, id):
return self.get("dirs", "dir", id)
def GetFileID(self, file, auto_set = 1):
return self.get_id("files", "file", file, auto_set)
def GetFile(self, id):
return self.get("files", "file", id)
def GetAuthorID(self, author, auto_set = 1):
return self.get_id("people", "who", author, auto_set)
def GetAuthor(self, id):
return self.get("people", "who", id)
def GetRepositoryID(self, repository, auto_set = 1):
return self.get_id("repositories", "repository", repository, auto_set)
def GetRepository(self, id):
return self.get("repositories", "repository", id)
def SQLGetDescriptionID(self, description, auto_set = 1):
## 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:
(id, ) = cursor.fetchone()
except TypeError:
if not auto_set:
return None
else:
return str(int(id))
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):
## attempt to retrieve from cache
hash = len(description)
try:
return self._desc_id_cache[hash][description]
except KeyError:
pass
id = self.SQLGetDescriptionID(description, auto_set)
if id == None:
return None
## add to cache
try:
temp = self._desc_id_cache[hash]
except KeyError:
temp = self._desc_id_cache[hash] = {}
temp[description] = id
return id
def GetDescription(self, id):
return self.get("descs", "description", id)
def GetRepositoryList(self):
return self.get_list("repositories", 1)
def GetBranchList(self):
return self.get_list("branches", 1)
def GetAuthorList(self):
return self.get_list("people", 1)
def AddCommitList(self, commit_list):
for commit in commit_list:
self.AddCommit(commit)
def AddCommit(self, commit):
## MORE TIME HELL: the MySQLdb module doesn't construct times
## correctly when created with TimestampFromTicks -- it doesn't
## account for daylight savings time, so we use Python's time
## module to do the conversion
temp = time.localtime(commit.GetTime())
ci_when = dbi.Timestamp(
temp[0], temp[1], temp[2], temp[3], temp[4], temp[5])
ci_type = commit.GetTypeString()
who_id = self.GetAuthorID(commit.GetAuthor())
repository_id = self.GetRepositoryID(commit.GetRepository())
directory_id = self.GetDirectoryID(commit.GetDirectory())
file_id = self.GetFileID(commit.GetFile())
revision = commit.GetRevision()
sticky_tag = "NULL"
branch_id = self.GetBranchID(commit.GetBranch())
plus_count = commit.GetPlusCount()
minus_count = commit.GetMinusCount()
description_id = self.GetDescriptionID(commit.GetDescription())
sql = "REPLACE INTO checkins"\
" (type,ci_when,whoid,repositoryid,dirid,fileid,revision,"\
" stickytag,branchid,addedlines,removedlines,descid)"\
"VALUES(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)"
sql_args = (ci_type, ci_when, who_id, repository_id,
directory_id, file_id, revision, sticky_tag, branch_id,
plus_count, minus_count, description_id)
cursor = self.db.cursor()
cursor.execute(sql, sql_args)
def SQLQueryListString(self, sqlString, query_entry_list):
sqlList = []
for query_entry in query_entry_list:
## figure out the correct match type
if query_entry.match == "exact":
match = "="
elif query_entry.match == "like":
match = " LIKE "
elif query_entry.match == "regex":
match = " REGEXP "
sqlList.append(sqlString % (match, query_entry.data))
return "(%s)" % (string.join(sqlList, " OR "))
def CreateSQLQueryString(self, query):
tableList = ["checkins"]
condList = []
## XXX: this is to exclude .ver files -- RN specific hack --JMP
tableList.append("files")
temp = "(checkins.fileid=files.id AND files.file NOT LIKE \"%.ver\")"
condList.append(temp)
## XXX
if len(query.repository_list):
tableList.append("repositories")
sql = "(checkins.repositoryid=repositories.id AND "\
"repositories.repository%s\"%s\")"
temp = self.SQLQueryListString(sql, query.repository_list)
condList.append(temp)
if len(query.branch_list):
tableList.append("branches")
sql = "(checkins.branchid=branches.id AND "\
"branches.branch%s\"%s\")"
temp = self.SQLQueryListString(sql, query.branch_list)
condList.append(temp)
if len(query.directory_list):
tableList.append("dirs")
sql = "(checkins.dirid=dirs.id AND dirs.dir%s\"%s\")"
temp = self.SQLQueryListString(sql, query.directory_list)
condList.append(temp)
if len(query.file_list):
tableList.append("files")
sql = "(checkins.fileid=files.id AND files.file%s\"%s\")"
temp = self.SQLQueryListString(sql, query.file_list)
condList.append(temp)
if len(query.author_list):
tableList.append("people")
sql = "(checkins.whoid=people.id AND people.who%s\"%s\")"
temp = self.SQLQueryListString(sql, query.author_list)
condList.append(temp)
if query.from_date:
temp = "(checkins.ci_when>=\"%s\")" % (str(query.from_date))
condList.append(temp)
if query.to_date:
temp = "(checkins.ci_when<=\"%s\")" % (str(query.to_date))
condList.append(temp)
if query.sort == "date":
order_by = "ORDER BY checkins.ci_when DESC"
elif query.sort == "author":
order_by = "ORDER BY checkins.whoid"
elif query.sort == "file":
order_by = "ORDER BY checkins.fileid"
## exclude duplicates from the table list
for table in tableList[:]:
while tableList.count(table) > 1:
tableList.remove(table)
tables = string.join(tableList, ",")
conditions = string.join(condList, " AND ")
## limit the number of rows requested or we could really slam
## a server with a large database
limit = ""
if cfg.cvsdb.row_limit:
limit = "LIMIT %s" % (str(cfg.cvsdb.row_limit))
sql = "SELECT checkins.* FROM %s WHERE %s %s %s" % (
tables, conditions, order_by, limit)
return sql
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
commit = CreateCommit()
## TIME, TIME, TIME is all fucked up; dateobject.gmticks()
## is broken, dateobject.ticks() returns somthing like
## GMT ticks, except it forgets about daylight savings
## time -- we handle it ourself in the following painful way
gmt_time = time.mktime(
(dbCI_When.year, dbCI_When.month, dbCI_When.day,
dbCI_When.hour, dbCI_When.minute, dbCI_When.second,
0, 0, dbCI_When.dst))
commit.SetTime(gmt_time)
commit.SetFile(self.GetFile(dbFileID))
commit.SetDirectory(self.GetDirectory(dbDirID))
commit.SetRevision(dbRevision)
commit.SetRepository(self.GetRepository(dbRepositoryID))
commit.SetAuthor(self.GetAuthor(dbAuthorID))
commit.SetBranch(self.GetBranch(dbBranchID))
commit.SetPlusCount(dbAddedLines)
commit.SetMinusCount(dbRemovedLines)
commit.SetDescription(self.GetDescription(dbDescID))
query.AddCommit(commit)
def CheckCommit(self, commit):
repository_id = self.GetRepositoryID(commit.GetRepository(), 0)
if repository_id == None:
return None
dir_id = self.GetDirectoryID(commit.GetDirectory(), 0)
if dir_id == None:
return None
file_id = self.GetFileID(commit.GetFile(), 0)
if file_id == None:
return None
sql = "SELECT * FROM checkins WHERE "\
" repositoryid=%s AND dirid=%s AND fileid=%s AND revision=%s"
sql_args = (repository_id, dir_id, file_id, commit.GetRevision())
cursor = self.db.cursor()
cursor.execute(sql, sql_args)
try:
(ci_type, ci_when, who_id, repository_id,
dir_id, file_id, revision, sticky_tag, branch_id,
plus_count, minus_count, description_id) = cursor.fetchone()
except TypeError:
return None
return commit
## the Commit class holds data on one commit, the representation is as
## close as possible to how it should be committed and retrieved to the
## database engine
class Commit:
## static constants for type of commit
CHANGE = 0
ADD = 1
REMOVE = 2
def __init__(self):
self.__directory = ''
self.__file = ''
self.__repository = ''
self.__revision = ''
self.__author = ''
self.__branch = ''
self.__pluscount = ''
self.__minuscount = ''
self.__description = ''
self.__gmt_time = 0.0
self.__type = Commit.CHANGE
def SetRepository(self, repository):
## clean up repository path; make sure it doesn't end with a
## path seperator
while len(repository) and repository[-1] == os.sep:
repository = repository[:-1]
self.__repository = repository
def GetRepository(self):
return self.__repository
def SetDirectory(self, dir):
## clean up directory path; make sure it doesn't begin
## or end with a path seperator
while len(dir) and dir[0] == os.sep:
dir = dir[1:]
while len(dir) and dir[-1] == os.sep:
dir = dir[:-1]
self.__directory = dir
def GetDirectory(self):
return self.__directory
def SetFile(self, file):
## clean up filename; make sure it doesn't begin
## or end with a path seperator
while len(file) and file[0] == os.sep:
file = file[1:]
while len(file) and file[-1] == os.sep:
file = file[:-1]
self.__file = file
def GetFile(self):
return self.__file
def SetRevision(self, revision):
self.__revision = revision
def GetRevision(self):
return self.__revision
def SetTime(self, gmt_time):
self.__gmt_time = float(gmt_time)
def GetTime(self):
return self.__gmt_time
def SetAuthor(self, author):
self.__author = author
def GetAuthor(self):
return self.__author
def SetBranch(self, branch):
if not branch:
self.__branch = ''
else:
self.__branch = branch
def GetBranch(self):
return self.__branch
def SetPlusCount(self, pluscount):
self.__pluscount = pluscount
def GetPlusCount(self):
return self.__pluscount
def SetMinusCount(self, minuscount):
self.__minuscount = minuscount
def GetMinusCount(self):
return self.__minuscount
def SetDescription(self, description):
self.__description = description
def GetDescription(self):
return self.__description
def SetTypeChange(self):
self.__type = Commit.CHANGE
def SetTypeAdd(self):
self.__type = Commit.ADD
def SetTypeRemove(self):
self.__type = Commit.REMOVE
def GetType(self):
return self.__type
def GetTypeString(self):
if self.__type == Commit.CHANGE:
return 'Change'
elif self.__type == Commit.ADD:
return 'Add'
elif self.__type == Commit.REMOVE:
return 'Remove'
## QueryEntry holds data on one match-type in the SQL database
## match is: "exact", "like", or "regex"
class QueryEntry:
def __init__(self, data, match):
self.data = data
self.match = match
## CheckinDatabaseQueryData is a object which contains the search parameters
## for a query to the CheckinDatabase
class CheckinDatabaseQuery:
def __init__(self):
## sorting
self.sort = "date"
## repository to query
self.repository_list = []
self.branch_list = []
self.directory_list = []
self.file_list = []
self.author_list = []
## date range in DBI 2.0 timedate objects
self.from_date = None
self.to_date = None
## list of commits -- filled in by CVS query
self.commit_list = []
## commit_cb provides a callback for commits as they
## are added
self.commit_cb = None
def SetRepository(self, repository, match = "exact"):
self.repository_list.append(QueryEntry(repository, match))
def SetBranch(self, branch, match = "exact"):
self.branch_list.append(QueryEntry(branch, match))
def SetDirectory(self, directory, match = "exact"):
self.directory_list.append(QueryEntry(directory, match))
def SetFile(self, file, match = "exact"):
self.file_list.append(QueryEntry(file, match))
def SetAuthor(self, author, match = "exact"):
self.author_list.append(QueryEntry(author, match))
def SetSortMethod(self, sort):
self.sort = sort
def SetFromDateObject(self, ticks):
self.from_date = dbi.TimestampFromTicks(ticks)
def SetToDateObject(self, ticks):
self.to_date = dbi.TimestampFromTicks(ticks)
def SetFromDateHoursAgo(self, hours_ago):
ticks = time.time() - (3600 * hours_ago)
self.from_date = dbi.TimestampFromTicks(ticks)
def SetFromDateDaysAgo(self, days_ago):
ticks = time.time() - (86400 * days_ago)
self.from_date = dbi.TimestampFromTicks(ticks)
def SetToDateDaysAgo(self, days_ago):
ticks = time.time() - (86400 * days_ago)
self.to_date = dbi.TimestampFromTicks(ticks)
def AddCommit(self, commit):
self.commit_list.append(commit)
if self.commit_cb:
self.commit_cb(commit)
def SetCommitCB(self, callback):
self.commit_cb = callback
##
## entrypoints
##
def CreateCheckinDatabase(host, user, passwd, database):
return CheckinDatabase(host, user, passwd, database)
def CreateCommit():
return Commit()
def CreateCheckinQuery():
return CheckinDatabaseQuery()
def ConnectDatabaseReadOnly():
global gCheckinDatabaseReadOnly
if gCheckinDatabaseReadOnly:
return gCheckinDatabaseReadOnly
gCheckinDatabaseReadOnly = CreateCheckinDatabase(
cfg.cvsdb.host,
cfg.cvsdb.readonly_user,
cfg.cvsdb.readonly_passwd,
cfg.cvsdb.database_name)
gCheckinDatabaseReadOnly.Connect()
return gCheckinDatabaseReadOnly
def ConnectDatabase():
global gCheckinDatabase
gCheckinDatabase = CreateCheckinDatabase(
cfg.cvsdb.host,
cfg.cvsdb.user,
cfg.cvsdb.passwd,
cfg.cvsdb.database_name)
gCheckinDatabase.Connect()
return gCheckinDatabase
def RLogDataToCommitList(repository, rlog_data):
commit_list = []
## the filename in rlog_data contains the entire path of the
## repository; we strip that out here
temp = rlog_data.filename[len(repository):]
directory, file = os.path.split(temp)
for rlog_entry in rlog_data.rlog_entry_list:
commit = CreateCommit()
commit.SetRepository(repository)
commit.SetDirectory(directory)
commit.SetFile(file)
commit.SetRevision(rlog_entry.revision)
commit.SetAuthor(rlog_entry.author)
commit.SetDescription(rlog_entry.description)
commit.SetTime(rlog_entry.time)
commit.SetPlusCount(rlog_entry.pluscount)
commit.SetMinusCount(rlog_entry.minuscount)
commit.SetBranch(rlog_data.LookupBranch(rlog_entry))
if rlog_entry.type == rlog_entry.CHANGE:
commit.SetTypeChange()
elif rlog_entry.type == rlog_entry.ADD:
commit.SetTypeAdd()
elif rlog_entry.type == rlog_entry.REMOVE:
commit.SetTypeRemove()
commit_list.append(commit)
return commit_list
def GetCommitListFromRCSFile(repository, filename):
try:
rlog_data = rlog.GetRLogData(cfg, filename)
except rlog.error, e:
raise error, e
commit_list = RLogDataToCommitList(repository, rlog_data)
return commit_list
def GetUnrecordedCommitList(repository, filename):
commit_list = GetCommitListFromRCSFile(repository, filename)
db = ConnectDatabase()
unrecorded_commit_list = []
for commit in commit_list:
result = db.CheckCommit(commit)
if not result:
unrecorded_commit_list.append(commit)
return unrecorded_commit_list

71
lib/dbi.py Normal file
View File

@@ -0,0 +1,71 @@
# -*- Mode: python -*-
#
# Copyright (C) 2000-2001 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 ViewCVS
# distribution or at http://viewcvs.sourceforge.net/license-1.html.
#
# Contact information:
# Greg Stein, PO Box 760, Palo Alto, CA, 94302
# gstein@lyra.org, http://viewcvs.sourceforge.net/
#
# -----------------------------------------------------------------------
#
import sys
import MySQLdb
dbi_error = "dbi error"
## make some checks in MySQLdb
_no_datetime = """\
ERROR: Your version of MySQLdb requires the mxDateTime module
for the Timestamp() and TimestampFromTicks() methods.
You will need to install mxDateTime to use the ViewCVS
database.
"""
if not hasattr(MySQLdb, "Timestamp") or \
not hasattr(MySQLdb, "TimestampFromTicks"):
sys.stderr.write(_no_datetime)
sys.exit(1)
class Cursor:
def __init__(self, mysql_cursor):
self.__cursor = mysql_cursor
def execute(self, *args):
apply(self.__cursor.execute, args)
def fetchone(self):
try:
row = self.__cursor.fetchone()
except IndexError:
row = None
return row
class Connection:
def __init__(self, host, user, passwd, db):
self.__mysql = MySQLdb.connect(
host=host, user=user, passwd=passwd, db=db)
def cursor(self):
return Cursor(self.__mysql.cursor())
def Timestamp(year, month, date, hour, minute, second):
return MySQLdb.Timestamp(year, month, date, hour, minute, second)
def TimestampFromTicks(ticks):
return MySQLdb.TimestampFromTicks(ticks)
def connect(host, user, passwd, db):
return Connection(host, user, passwd, db)

42
lib/debug.py Normal file
View File

@@ -0,0 +1,42 @@
# -*- Mode: python -*-
#
# Copyright (C) 2000-2001 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 ViewCVS
# distribution or at http://viewcvs.sourceforge.net/license-1.html.
#
# Contact information:
# Greg Stein, PO Box 760, Palo Alto, CA, 94302
# gstein@lyra.org, http://viewcvs.sourceforge.net/
#
# -----------------------------------------------------------------------
#
# Note: a t_start/t_end pair consumes about 0.00005 seconds on a P3/700.
# the lambda form (when debugging is disabled) should be even faster.
#
if 0:
import time
_timers = { }
_times = { }
def t_start(which):
_timers[which] = time.time()
def t_end(which):
t = time.time() - _timers[which]
if _times.has_key(which):
_times[which] = _times[which] + t
else:
_times[which] = t
def dump():
for name, value in _times.items():
print '%s: %.6f<br>' % (name, value)
else:
t_start = t_end = dump = lambda *args: None

786
lib/difflib.py Executable file
View File

@@ -0,0 +1,786 @@
#! /usr/bin/env python
# Backported to Python 1.5.2 for the ViewCVS project by pf@artcom-gmbh.de
# 24-Dec-2001, original version "stolen" from Python-2.1.1
"""
Module difflib -- helpers for computing deltas between objects.
Function get_close_matches(word, possibilities, n=3, cutoff=0.6):
Use SequenceMatcher to return list of the best "good enough" matches.
word is a sequence for which close matches are desired (typically a
string).
possibilities is a list of sequences against which to match word
(typically a list of strings).
Optional arg n (default 3) is the maximum number of close matches to
return. n must be > 0.
Optional arg cutoff (default 0.6) is a float in [0, 1]. Possibilities
that don't score at least that similar to word are ignored.
The best (no more than n) matches among the possibilities are returned
in a list, sorted by similarity score, most similar first.
>>> get_close_matches("appel", ["ape", "apple", "peach", "puppy"])
['apple', 'ape']
>>> import keyword
>>> get_close_matches("wheel", keyword.kwlist)
['while']
>>> get_close_matches("apple", keyword.kwlist)
[]
>>> get_close_matches("accept", keyword.kwlist)
['except']
Class SequenceMatcher
SequenceMatcher is a flexible class for comparing pairs of sequences of any
type, so long as the sequence elements are hashable. The basic algorithm
predates, and is a little fancier than, an algorithm published in the late
1980's by Ratcliff and Obershelp under the hyperbolic name "gestalt pattern
matching". The basic idea is to find the longest contiguous matching
subsequence that contains no "junk" elements (R-O doesn't address junk).
The same idea is then applied recursively to the pieces of the sequences to
the left and to the right of the matching subsequence. This does not yield
minimal edit sequences, but does tend to yield matches that "look right"
to people.
Example, comparing two strings, and considering blanks to be "junk":
>>> s = SequenceMatcher(lambda x: x == " ",
... "private Thread currentThread;",
... "private volatile Thread currentThread;")
>>>
.ratio() returns a float in [0, 1], measuring the "similarity" of the
sequences. As a rule of thumb, a .ratio() value over 0.6 means the
sequences are close matches:
>>> print round(s.ratio(), 3)
0.866
>>>
If you're only interested in where the sequences match,
.get_matching_blocks() is handy:
>>> for block in s.get_matching_blocks():
... print "a[%d] and b[%d] match for %d elements" % block
a[0] and b[0] match for 8 elements
a[8] and b[17] match for 6 elements
a[14] and b[23] match for 15 elements
a[29] and b[38] match for 0 elements
Note that the last tuple returned by .get_matching_blocks() is always a
dummy, (len(a), len(b), 0), and this is the only case in which the last
tuple element (number of elements matched) is 0.
If you want to know how to change the first sequence into the second, use
.get_opcodes():
>>> for opcode in s.get_opcodes():
... print "%6s a[%d:%d] b[%d:%d]" % opcode
equal a[0:8] b[0:8]
insert a[8:8] b[8:17]
equal a[8:14] b[17:23]
equal a[14:29] b[23:38]
See Tools/scripts/ndiff.py for a fancy human-friendly file differencer,
which uses SequenceMatcher both to view files as sequences of lines, and
lines as sequences of characters.
See also function get_close_matches() in this module, which shows how
simple code building on SequenceMatcher can be used to do useful work.
Timing: Basic R-O is cubic time worst case and quadratic time expected
case. SequenceMatcher is quadratic time for the worst case and has
expected-case behavior dependent in a complicated way on how many
elements the sequences have in common; best case time is linear.
SequenceMatcher methods:
__init__(isjunk=None, a='', b='')
Construct a SequenceMatcher.
Optional arg isjunk is None (the default), or a one-argument function
that takes a sequence element and returns true iff the element is junk.
None is equivalent to passing "lambda x: 0", i.e. no elements are
considered to be junk. For example, pass
lambda x: x in " \\t"
if you're comparing lines as sequences of characters, and don't want to
synch up on blanks or hard tabs.
Optional arg a is the first of two sequences to be compared. By
default, an empty string. The elements of a must be hashable.
Optional arg b is the second of two sequences to be compared. By
default, an empty string. The elements of b must be hashable.
set_seqs(a, b)
Set the two sequences to be compared.
>>> s = SequenceMatcher()
>>> s.set_seqs("abcd", "bcde")
>>> s.ratio()
0.75
set_seq1(a)
Set the first sequence to be compared.
The second sequence to be compared is not changed.
>>> s = SequenceMatcher(None, "abcd", "bcde")
>>> s.ratio()
0.75
>>> s.set_seq1("bcde")
>>> s.ratio()
1.0
>>>
SequenceMatcher computes and caches detailed information about the
second sequence, so if you want to compare one sequence S against many
sequences, use .set_seq2(S) once and call .set_seq1(x) repeatedly for
each of the other sequences.
See also set_seqs() and set_seq2().
set_seq2(b)
Set the second sequence to be compared.
The first sequence to be compared is not changed.
>>> s = SequenceMatcher(None, "abcd", "bcde")
>>> s.ratio()
0.75
>>> s.set_seq2("abcd")
>>> s.ratio()
1.0
>>>
SequenceMatcher computes and caches detailed information about the
second sequence, so if you want to compare one sequence S against many
sequences, use .set_seq2(S) once and call .set_seq1(x) repeatedly for
each of the other sequences.
See also set_seqs() and set_seq1().
find_longest_match(alo, ahi, blo, bhi)
Find longest matching block in a[alo:ahi] and b[blo:bhi].
If isjunk is not defined:
Return (i,j,k) such that a[i:i+k] is equal to b[j:j+k], where
alo <= i <= i+k <= ahi
blo <= j <= j+k <= bhi
and for all (i',j',k') meeting those conditions,
k >= k'
i <= i'
and if i == i', j <= j'
In other words, of all maximal matching blocks, return one that starts
earliest in a, and of all those maximal matching blocks that start
earliest in a, return the one that starts earliest in b.
>>> s = SequenceMatcher(None, " abcd", "abcd abcd")
>>> s.find_longest_match(0, 5, 0, 9)
(0, 4, 5)
If isjunk is defined, first the longest matching block is determined as
above, but with the additional restriction that no junk element appears
in the block. Then that block is extended as far as possible by
matching (only) junk elements on both sides. So the resulting block
never matches on junk except as identical junk happens to be adjacent
to an "interesting" match.
Here's the same example as before, but considering blanks to be junk.
That prevents " abcd" from matching the " abcd" at the tail end of the
second sequence directly. Instead only the "abcd" can match, and
matches the leftmost "abcd" in the second sequence:
>>> s = SequenceMatcher(lambda x: x==" ", " abcd", "abcd abcd")
>>> s.find_longest_match(0, 5, 0, 9)
(1, 0, 4)
If no blocks match, return (alo, blo, 0).
>>> s = SequenceMatcher(None, "ab", "c")
>>> s.find_longest_match(0, 2, 0, 1)
(0, 0, 0)
get_matching_blocks()
Return list of triples describing matching subsequences.
Each triple is of the form (i, j, n), and means that
a[i:i+n] == b[j:j+n]. The triples are monotonically increasing in i
and in j.
The last triple is a dummy, (len(a), len(b), 0), and is the only triple
with n==0.
>>> s = SequenceMatcher(None, "abxcd", "abcd")
>>> s.get_matching_blocks()
[(0, 0, 2), (3, 2, 2), (5, 4, 0)]
get_opcodes()
Return list of 5-tuples describing how to turn a into b.
Each tuple is of the form (tag, i1, i2, j1, j2). The first tuple has
i1 == j1 == 0, and remaining tuples have i1 == the i2 from the tuple
preceding it, and likewise for j1 == the previous j2.
The tags are strings, with these meanings:
'replace': a[i1:i2] should be replaced by b[j1:j2]
'delete': a[i1:i2] should be deleted.
Note that j1==j2 in this case.
'insert': b[j1:j2] should be inserted at a[i1:i1].
Note that i1==i2 in this case.
'equal': a[i1:i2] == b[j1:j2]
>>> a = "qabxcd"
>>> b = "abycdf"
>>> s = SequenceMatcher(None, a, b)
>>> for tag, i1, i2, j1, j2 in s.get_opcodes():
... print ("%7s a[%d:%d] (%s) b[%d:%d] (%s)" %
... (tag, i1, i2, a[i1:i2], j1, j2, b[j1:j2]))
delete a[0:1] (q) b[0:0] ()
equal a[1:3] (ab) b[0:2] (ab)
replace a[3:4] (x) b[2:3] (y)
equal a[4:6] (cd) b[3:5] (cd)
insert a[6:6] () b[5:6] (f)
ratio()
Return a measure of the sequences' similarity (float in [0,1]).
Where T is the total number of elements in both sequences, and M is the
number of matches, this is 2,0*M / T. Note that this is 1 if the
sequences are identical, and 0 if they have nothing in common.
.ratio() is expensive to compute if you haven't already computed
.get_matching_blocks() or .get_opcodes(), in which case you may want to
try .quick_ratio() or .real_quick_ratio() first to get an upper bound.
>>> s = SequenceMatcher(None, "abcd", "bcde")
>>> s.ratio()
0.75
>>> s.quick_ratio()
0.75
>>> s.real_quick_ratio()
1.0
quick_ratio()
Return an upper bound on .ratio() relatively quickly.
This isn't defined beyond that it is an upper bound on .ratio(), and
is faster to compute.
real_quick_ratio():
Return an upper bound on ratio() very quickly.
This isn't defined beyond that it is an upper bound on .ratio(), and
is faster to compute than either .ratio() or .quick_ratio().
"""
TRACE = 0
class SequenceMatcher:
def __init__(self, isjunk=None, a='', b=''):
"""Construct a SequenceMatcher.
Optional arg isjunk is None (the default), or a one-argument
function that takes a sequence element and returns true iff the
element is junk. None is equivalent to passing "lambda x: 0", i.e.
no elements are considered to be junk. For example, pass
lambda x: x in " \\t"
if you're comparing lines as sequences of characters, and don't
want to synch up on blanks or hard tabs.
Optional arg a is the first of two sequences to be compared. By
default, an empty string. The elements of a must be hashable. See
also .set_seqs() and .set_seq1().
Optional arg b is the second of two sequences to be compared. By
default, an empty string. The elements of b must be hashable. See
also .set_seqs() and .set_seq2().
"""
# Members:
# a
# first sequence
# b
# second sequence; differences are computed as "what do
# we need to do to 'a' to change it into 'b'?"
# b2j
# for x in b, b2j[x] is a list of the indices (into b)
# at which x appears; junk elements do not appear
# b2jhas
# b2j.has_key
# fullbcount
# for x in b, fullbcount[x] == the number of times x
# appears in b; only materialized if really needed (used
# only for computing quick_ratio())
# matching_blocks
# a list of (i, j, k) triples, where a[i:i+k] == b[j:j+k];
# ascending & non-overlapping in i and in j; terminated by
# a dummy (len(a), len(b), 0) sentinel
# opcodes
# a list of (tag, i1, i2, j1, j2) tuples, where tag is
# one of
# 'replace' a[i1:i2] should be replaced by b[j1:j2]
# 'delete' a[i1:i2] should be deleted
# 'insert' b[j1:j2] should be inserted
# 'equal' a[i1:i2] == b[j1:j2]
# isjunk
# a user-supplied function taking a sequence element and
# returning true iff the element is "junk" -- this has
# subtle but helpful effects on the algorithm, which I'll
# get around to writing up someday <0.9 wink>.
# DON'T USE! Only __chain_b uses this. Use isbjunk.
# isbjunk
# for x in b, isbjunk(x) == isjunk(x) but much faster;
# it's really the has_key method of a hidden dict.
# DOES NOT WORK for x in a!
self.isjunk = isjunk
self.a = self.b = None
self.set_seqs(a, b)
def set_seqs(self, a, b):
"""Set the two sequences to be compared.
>>> s = SequenceMatcher()
>>> s.set_seqs("abcd", "bcde")
>>> s.ratio()
0.75
"""
self.set_seq1(a)
self.set_seq2(b)
def set_seq1(self, a):
"""Set the first sequence to be compared.
The second sequence to be compared is not changed.
>>> s = SequenceMatcher(None, "abcd", "bcde")
>>> s.ratio()
0.75
>>> s.set_seq1("bcde")
>>> s.ratio()
1.0
>>>
SequenceMatcher computes and caches detailed information about the
second sequence, so if you want to compare one sequence S against
many sequences, use .set_seq2(S) once and call .set_seq1(x)
repeatedly for each of the other sequences.
See also set_seqs() and set_seq2().
"""
if a is self.a:
return
self.a = a
self.matching_blocks = self.opcodes = None
def set_seq2(self, b):
"""Set the second sequence to be compared.
The first sequence to be compared is not changed.
>>> s = SequenceMatcher(None, "abcd", "bcde")
>>> s.ratio()
0.75
>>> s.set_seq2("abcd")
>>> s.ratio()
1.0
>>>
SequenceMatcher computes and caches detailed information about the
second sequence, so if you want to compare one sequence S against
many sequences, use .set_seq2(S) once and call .set_seq1(x)
repeatedly for each of the other sequences.
See also set_seqs() and set_seq1().
"""
if b is self.b:
return
self.b = b
self.matching_blocks = self.opcodes = None
self.fullbcount = None
self.__chain_b()
# For each element x in b, set b2j[x] to a list of the indices in
# b where x appears; the indices are in increasing order; note that
# the number of times x appears in b is len(b2j[x]) ...
# when self.isjunk is defined, junk elements don't show up in this
# map at all, which stops the central find_longest_match method
# from starting any matching block at a junk element ...
# also creates the fast isbjunk function ...
# note that this is only called when b changes; so for cross-product
# kinds of matches, it's best to call set_seq2 once, then set_seq1
# repeatedly
def __chain_b(self):
# Because isjunk is a user-defined (not C) function, and we test
# for junk a LOT, it's important to minimize the number of calls.
# Before the tricks described here, __chain_b was by far the most
# time-consuming routine in the whole module! If anyone sees
# Jim Roskind, thank him again for profile.py -- I never would
# have guessed that.
# The first trick is to build b2j ignoring the possibility
# of junk. I.e., we don't call isjunk at all yet. Throwing
# out the junk later is much cheaper than building b2j "right"
# from the start.
b = self.b
self.b2j = b2j = {}
self.b2jhas = b2jhas = b2j.has_key
for i in xrange(len(b)):
elt = b[i]
if b2jhas(elt):
b2j[elt].append(i)
else:
b2j[elt] = [i]
# Now b2j.keys() contains elements uniquely, and especially when
# the sequence is a string, that's usually a good deal smaller
# than len(string). The difference is the number of isjunk calls
# saved.
isjunk, junkdict = self.isjunk, {}
if isjunk:
for elt in b2j.keys():
if isjunk(elt):
junkdict[elt] = 1 # value irrelevant; it's a set
del b2j[elt]
# Now for x in b, isjunk(x) == junkdict.has_key(x), but the
# latter is much faster. Note too that while there may be a
# lot of junk in the sequence, the number of *unique* junk
# elements is probably small. So the memory burden of keeping
# this dict alive is likely trivial compared to the size of b2j.
self.isbjunk = junkdict.has_key
def find_longest_match(self, alo, ahi, blo, bhi):
"""Find longest matching block in a[alo:ahi] and b[blo:bhi].
If isjunk is not defined:
Return (i,j,k) such that a[i:i+k] is equal to b[j:j+k], where
alo <= i <= i+k <= ahi
blo <= j <= j+k <= bhi
and for all (i',j',k') meeting those conditions,
k >= k'
i <= i'
and if i == i', j <= j'
In other words, of all maximal matching blocks, return one that
starts earliest in a, and of all those maximal matching blocks that
start earliest in a, return the one that starts earliest in b.
>>> s = SequenceMatcher(None, " abcd", "abcd abcd")
>>> s.find_longest_match(0, 5, 0, 9)
(0, 4, 5)
If isjunk is defined, first the longest matching block is
determined as above, but with the additional restriction that no
junk element appears in the block. Then that block is extended as
far as possible by matching (only) junk elements on both sides. So
the resulting block never matches on junk except as identical junk
happens to be adjacent to an "interesting" match.
Here's the same example as before, but considering blanks to be
junk. That prevents " abcd" from matching the " abcd" at the tail
end of the second sequence directly. Instead only the "abcd" can
match, and matches the leftmost "abcd" in the second sequence:
>>> s = SequenceMatcher(lambda x: x==" ", " abcd", "abcd abcd")
>>> s.find_longest_match(0, 5, 0, 9)
(1, 0, 4)
If no blocks match, return (alo, blo, 0).
>>> s = SequenceMatcher(None, "ab", "c")
>>> s.find_longest_match(0, 2, 0, 1)
(0, 0, 0)
"""
# CAUTION: stripping common prefix or suffix would be incorrect.
# E.g.,
# ab
# acab
# Longest matching block is "ab", but if common prefix is
# stripped, it's "a" (tied with "b"). UNIX(tm) diff does so
# strip, so ends up claiming that ab is changed to acab by
# inserting "ca" in the middle. That's minimal but unintuitive:
# "it's obvious" that someone inserted "ac" at the front.
# Windiff ends up at the same place as diff, but by pairing up
# the unique 'b's and then matching the first two 'a's.
a, b, b2j, isbjunk = self.a, self.b, self.b2j, self.isbjunk
besti, bestj, bestsize = alo, blo, 0
# find longest junk-free match
# during an iteration of the loop, j2len[j] = length of longest
# junk-free match ending with a[i-1] and b[j]
j2len = {}
nothing = []
for i in xrange(alo, ahi):
# look at all instances of a[i] in b; note that because
# b2j has no junk keys, the loop is skipped if a[i] is junk
j2lenget = j2len.get
newj2len = {}
for j in b2j.get(a[i], nothing):
# a[i] matches b[j]
if j < blo:
continue
if j >= bhi:
break
k = newj2len[j] = j2lenget(j-1, 0) + 1
if k > bestsize:
besti, bestj, bestsize = i-k+1, j-k+1, k
j2len = newj2len
# Now that we have a wholly interesting match (albeit possibly
# empty!), we may as well suck up the matching junk on each
# side of it too. Can't think of a good reason not to, and it
# saves post-processing the (possibly considerable) expense of
# figuring out what to do with it. In the case of an empty
# interesting match, this is clearly the right thing to do,
# because no other kind of match is possible in the regions.
while besti > alo and bestj > blo and \
isbjunk(b[bestj-1]) and \
a[besti-1] == b[bestj-1]:
besti, bestj, bestsize = besti-1, bestj-1, bestsize+1
while besti+bestsize < ahi and bestj+bestsize < bhi and \
isbjunk(b[bestj+bestsize]) and \
a[besti+bestsize] == b[bestj+bestsize]:
bestsize = bestsize + 1
if TRACE:
print "get_matching_blocks", alo, ahi, blo, bhi
print " returns", besti, bestj, bestsize
return besti, bestj, bestsize
def get_matching_blocks(self):
"""Return list of triples describing matching subsequences.
Each triple is of the form (i, j, n), and means that
a[i:i+n] == b[j:j+n]. The triples are monotonically increasing in
i and in j.
The last triple is a dummy, (len(a), len(b), 0), and is the only
triple with n==0.
>>> s = SequenceMatcher(None, "abxcd", "abcd")
>>> s.get_matching_blocks()
[(0, 0, 2), (3, 2, 2), (5, 4, 0)]
"""
if self.matching_blocks is not None:
return self.matching_blocks
self.matching_blocks = []
la, lb = len(self.a), len(self.b)
self.__helper(0, la, 0, lb, self.matching_blocks)
self.matching_blocks.append( (la, lb, 0) )
if TRACE:
print '*** matching blocks', self.matching_blocks
return self.matching_blocks
# builds list of matching blocks covering a[alo:ahi] and
# b[blo:bhi], appending them in increasing order to answer
def __helper(self, alo, ahi, blo, bhi, answer):
i, j, k = x = self.find_longest_match(alo, ahi, blo, bhi)
# a[alo:i] vs b[blo:j] unknown
# a[i:i+k] same as b[j:j+k]
# a[i+k:ahi] vs b[j+k:bhi] unknown
if k:
if alo < i and blo < j:
self.__helper(alo, i, blo, j, answer)
answer.append(x)
if i+k < ahi and j+k < bhi:
self.__helper(i+k, ahi, j+k, bhi, answer)
def get_opcodes(self):
"""Return list of 5-tuples describing how to turn a into b.
Each tuple is of the form (tag, i1, i2, j1, j2). The first tuple
has i1 == j1 == 0, and remaining tuples have i1 == the i2 from the
tuple preceding it, and likewise for j1 == the previous j2.
The tags are strings, with these meanings:
'replace': a[i1:i2] should be replaced by b[j1:j2]
'delete': a[i1:i2] should be deleted.
Note that j1==j2 in this case.
'insert': b[j1:j2] should be inserted at a[i1:i1].
Note that i1==i2 in this case.
'equal': a[i1:i2] == b[j1:j2]
>>> a = "qabxcd"
>>> b = "abycdf"
>>> s = SequenceMatcher(None, a, b)
>>> for tag, i1, i2, j1, j2 in s.get_opcodes():
... print ("%7s a[%d:%d] (%s) b[%d:%d] (%s)" %
... (tag, i1, i2, a[i1:i2], j1, j2, b[j1:j2]))
delete a[0:1] (q) b[0:0] ()
equal a[1:3] (ab) b[0:2] (ab)
replace a[3:4] (x) b[2:3] (y)
equal a[4:6] (cd) b[3:5] (cd)
insert a[6:6] () b[5:6] (f)
"""
if self.opcodes is not None:
return self.opcodes
i = j = 0
self.opcodes = answer = []
for ai, bj, size in self.get_matching_blocks():
# invariant: we've pumped out correct diffs to change
# a[:i] into b[:j], and the next matching block is
# a[ai:ai+size] == b[bj:bj+size]. So we need to pump
# out a diff to change a[i:ai] into b[j:bj], pump out
# the matching block, and move (i,j) beyond the match
tag = ''
if i < ai and j < bj:
tag = 'replace'
elif i < ai:
tag = 'delete'
elif j < bj:
tag = 'insert'
if tag:
answer.append( (tag, i, ai, j, bj) )
i, j = ai+size, bj+size
# the list of matching blocks is terminated by a
# sentinel with size 0
if size:
answer.append( ('equal', ai, i, bj, j) )
return answer
def ratio(self):
"""Return a measure of the sequences' similarity (float in [0,1]).
Where T is the total number of elements in both sequences, and
M is the number of matches, this is 2,0*M / T.
Note that this is 1 if the sequences are identical, and 0 if
they have nothing in common.
.ratio() is expensive to compute if you haven't already computed
.get_matching_blocks() or .get_opcodes(), in which case you may
want to try .quick_ratio() or .real_quick_ratio() first to get an
upper bound.
>>> s = SequenceMatcher(None, "abcd", "bcde")
>>> s.ratio()
0.75
>>> s.quick_ratio()
0.75
>>> s.real_quick_ratio()
1.0
"""
matches = reduce(lambda sum, triple: sum + triple[-1],
self.get_matching_blocks(), 0)
return 2.0 * matches / (len(self.a) + len(self.b))
def quick_ratio(self):
"""Return an upper bound on ratio() relatively quickly.
This isn't defined beyond that it is an upper bound on .ratio(), and
is faster to compute.
"""
# viewing a and b as multisets, set matches to the cardinality
# of their intersection; this counts the number of matches
# without regard to order, so is clearly an upper bound
if self.fullbcount is None:
self.fullbcount = fullbcount = {}
for elt in self.b:
fullbcount[elt] = fullbcount.get(elt, 0) + 1
fullbcount = self.fullbcount
# avail[x] is the number of times x appears in 'b' less the
# number of times we've seen it in 'a' so far ... kinda
avail = {}
availhas, matches = avail.has_key, 0
for elt in self.a:
if availhas(elt):
numb = avail[elt]
else:
numb = fullbcount.get(elt, 0)
avail[elt] = numb - 1
if numb > 0:
matches = matches + 1
return 2.0 * matches / (len(self.a) + len(self.b))
def real_quick_ratio(self):
"""Return an upper bound on ratio() very quickly.
This isn't defined beyond that it is an upper bound on .ratio(), and
is faster to compute than either .ratio() or .quick_ratio().
"""
la, lb = len(self.a), len(self.b)
# can't have more matches than the number of elements in the
# shorter sequence
return 2.0 * min(la, lb) / (la + lb)
def get_close_matches(word, possibilities, n=3, cutoff=0.6):
"""Use SequenceMatcher to return list of the best "good enough" matches.
word is a sequence for which close matches are desired (typically a
string).
possibilities is a list of sequences against which to match word
(typically a list of strings).
Optional arg n (default 3) is the maximum number of close matches to
return. n must be > 0.
Optional arg cutoff (default 0.6) is a float in [0, 1]. Possibilities
that don't score at least that similar to word are ignored.
The best (no more than n) matches among the possibilities are returned
in a list, sorted by similarity score, most similar first.
>>> get_close_matches("appel", ["ape", "apple", "peach", "puppy"])
['apple', 'ape']
>>> import keyword
>>> get_close_matches("wheel", keyword.kwlist)
['while']
>>> get_close_matches("apple", keyword.kwlist)
[]
>>> get_close_matches("accept", keyword.kwlist)
['except']
"""
if not n > 0:
raise ValueError("n must be > 0: " + `n`)
if not 0.0 <= cutoff <= 1.0:
raise ValueError("cutoff must be in [0.0, 1.0]: " + `cutoff`)
result = []
s = SequenceMatcher()
s.set_seq2(word)
for x in possibilities:
s.set_seq1(x)
if s.real_quick_ratio() >= cutoff and \
s.quick_ratio() >= cutoff and \
s.ratio() >= cutoff:
result.append((s.ratio(), x))
# Sort by score.
result.sort()
# Retain only the best n.
result = result[-n:]
# Move best-scorer to head of list.
result.reverse()
# Strip scores.
# Python 2.x list comprehensions: return [x for score, x in result]
return_result = []
for score, x in result:
return_result.append(x)
return return_result
def _test():
import doctest, difflib
return doctest.testmod(difflib)
if __name__ == "__main__":
_test()

523
lib/ezt.py Normal file
View File

@@ -0,0 +1,523 @@
#!/usr/bin/env python
"""ezt.py -- easy templating
ezt templates are very similar to standard HTML files. But additionaly
they contain directives sprinkled in between. With these directives
it possible to generate the dynamic content from the ezt templates.
These directives are enclosed in square brackets. If you are a
C-programmer, you might be familar with the #ifdef directives of the
C preprocessor 'cpp'. ezt provides a similar concept for HTML. Additionally
EZT has a 'for' directive, which allows to iterate (repeat) certain
subsections of the template according to sequence of data items
provided by the application.
The HTML rendering is performed by the method generate() of the Template
class. Building template instances can either be done using external
EZT files (convention: use the suffix .ezt for such files):
>>> template = Template("../templates/log.ezt")
or by calling the parse() method of a template instance directly with
a EZT template string:
>>> template = Template()
>>> template.parse('''<html><head>
... <title>[title_string]</title></head>
... <body><h1>[title_string]</h1>
... [for a_sequence] <p>[a_sequence]</p>
... [end] <hr>
... The [person] is [if-any state]in[else]out[end].
... </body>
... </html>
... ''')
The application should build a dictionary 'data' and pass it together
with the output fileobject to the templates generate method:
>>> data = {'title_string' : "A Dummy Page",
... 'a_sequence' : ['list item 1', 'list item 2', 'another element'],
... 'person': "doctor",
... 'state' : None }
>>> import sys
>>> template.generate(sys.stdout, data)
<html><head>
<title>A Dummy Page</title></head>
<body><h1>A Dummy Page</h1>
<p>list item 1</p>
<p>list item 2</p>
<p>another element</p>
<hr>
The doctor is out.
</body>
</html>
Directives
==========
Several directives allow the use of dotted qualified names refering to objects
or attributes of objects contained in the data dictionary given to the
.generate() method.
Simple directives
-----------------
[QUAL_NAME]
This directive is simply replaced by the value of identifier from the data
dictionary. QUAL_NAME might be a dotted qualified name refering to some
instance attribute of objects contained in the dats dictionary.
Numbers are converted to string though.
[include "filename"] or [include QUAL_NAME]
This directive is replaced by content of the named include file.
Block directives
----------------
[for QUAL_NAME] ... [end]
The text within the [for ...] directive and the corresponding [end]
is repeated for each element in the sequence referred to by the qualified
name in the for directive. Within the for block this identifiers now
refers to the actual item indexed by this loop iteration.
[if-any QUAL_NAME] ... [else] ... [end]
Test if the value QUAL_NAME is not None or an empty string or list.
The [else] clause is optional. CAUTION: Numeric values are converted to string,
so if QUAL_NAME refers to a numeric value 0, the then-clause is
substituted!
[if-index odd] ... [else] ... [end]
[if-index even] ... [else] ... [end]
[if-index first] ... [else] ... [end]
[if-index last] ... [else] ... [end]
[if-index NUMBER] ... [else] ... [end]
These five directives work similar to [if-any], but are only useful
within a [for ...]-block (see above). The odd/even directives are
for example useful to choose different background colors for adjacent rows
in a table. Similar the first/last directives might be used to
remove certain parts (for example "Diff to previous" doesn't make sense,
if there is no previous).
[is QUAL_NAME STRING] ... [else] ... [end]
[is QUAL_NAME QUAL_NAME] ... [else] ... [end]
The [is ...] directive is similar to the other conditional directives
above. But it allows to compare two value references or a value reference
with some constant string.
"""
#
# Copyright (C) 2001 Greg Stein. All Rights Reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
#
# This software is maintained by Greg and is available at:
# http://viewcvs.sourceforge.net/
# it is also used by the following projects:
# http://edna.sourceforge.net/
#
import string
import re
from types import StringType, IntType, FloatType
import os
#
# This regular expression matches three alternatives:
# expr: DIRECTIVE | BRACKET | COMMENT
# DIRECTIVE: '[' ITEM (whitespace ITEM)* ']
# ITEM: STRING | NAME
# STRING: '"' (not-slash-or-dquote | '\' anychar)* '"'
# NAME: (alphanum | '_' | '-' | '.')+
# BRACKET: '[[]'
# COMMENT: '[#' not-rbracket* ']'
#
# When used with the split() method, the return value will be composed of
# non-matching text and the two paren groups (DIRECTIVE and BRACKET). Since
# the COMMENT matches are not placed into a group, they are considered a
# "splitting" value and simply dropped.
#
_item = r'(?:"(?:[^\\"]|\\.)*"|[-\w.]+)'
_re_parse = re.compile(r'\[(%s(?: +%s)*)\]|(\[\[\])|\[#[^\]]*\]' % (_item, _item))
_re_args = re.compile(r'"(?:[^\\"]|\\.)*"|[-\w.]+')
# block commands and their argument counts
_block_cmd_specs = { 'if-any':1, 'if-index':2, 'for':1, 'is':2 }
_block_cmds = _block_cmd_specs.keys()
# two regular expresssions for compressing whitespace. the first is used to
# compress any whitespace including a newline into a single newline. the
# second regex is used to compress runs of whitespace into a single space.
_re_newline = re.compile('[ \t\r\f\v]*\n\\s*')
_re_whitespace = re.compile(r'\s\s+')
# this regex is used to substitute arguments into a value. we split the value,
# replace the relevant pieces, and then put it all back together. splitting
# will produce a list of: TEXT ( splitter TEXT )*. splitter will be '%' or
# an integer.
_re_subst = re.compile('%(%|[0-9]+)')
class Template:
def __init__(self, fname=None):
if fname:
self.parse_file(fname)
def parse_file(self, fname):
"""fname -> a string object with pathname of file containg an EZT template.
"""
self.program = self._parse_file(fname)
def parse(self, text):
"""text -> a string object containing the HTML template.
parse the template program into: (TEXT DIRECTIVE BRACKET)* TEXT
DIRECTIVE will be '[directive]' or None
BRACKET will be '[[]' or None
"""
self.program = self._parse(text)
def generate(self, fp, data):
ctx = _context()
ctx.data = data
ctx.for_index = { }
self._execute(self.program, fp, ctx)
def _parse_file(self, fname, for_names=None, file_args=()):
return self._parse(open(fname, "rt").read(), for_names, file_args,
os.path.dirname(fname))
def _parse(self, text, for_names=None, file_args=(), base=None):
"""text -> string object containing the HTML template.
This is a private helper function doing the real work for method parse.
It returns the parsed template as a 'program'. This program is a sequence
made out of strings or (function, argument) 2-tuples.
Note: comment directives [# ...] are automatically dropped by _re_parse.
"""
parts = _re_parse.split(text)
program = [ ]
stack = [ ]
if not for_names:
for_names = [ ]
for i in range(len(parts)):
piece = parts[i]
which = i % 3 # discriminate between: TEXT DIRECTIVE BRACKET
if which == 0:
# TEXT. append if non-empty.
if piece:
piece = _re_whitespace.sub(' ', _re_newline.sub('\n', piece))
program.append(piece)
elif which == 2:
# BRACKET directive. append '[' if present.
if piece:
program.append('[')
elif piece:
# DIRECTIVE is present.
args = _re_args.findall(piece)
cmd = args[0]
if cmd == 'else':
if len(args) > 1:
raise ArgCountSyntaxError()
### check: don't allow for 'for' cmd
idx = stack[-1][1]
true_section = program[idx:]
del program[idx:]
stack[-1][3] = true_section
elif cmd == 'end':
if len(args) > 1:
raise ArgCountSyntaxError()
# note: true-section may be None
cmd, idx, args, true_section = stack.pop()
else_section = program[idx:]
func = getattr(self, '_cmd_' + re.sub('-', '_', cmd))
program[idx:] = [ (func, (args, true_section, else_section)) ]
if cmd == 'for':
for_names.pop()
elif cmd in _block_cmds:
if len(args) > _block_cmd_specs[cmd] + 1:
raise ArgCountSyntaxError()
### this assumes arg1 is always a ref
args[1] = _prepare_ref(args[1], for_names, file_args)
# handle arg2 for the 'is' command
if cmd == 'is':
args[2] = _prepare_ref(args[2], for_names, file_args)
elif cmd == 'for':
for_names.append(args[1][0])
# remember the cmd, current pos, args, and a section placeholder
stack.append([cmd, len(program), args[1:], None])
elif cmd == 'include':
if args[1][0] == '"':
include_filename = args[1][1:-1]
if base:
include_filename = os.path.join(base, include_filename)
f_args = [ ]
for arg in args[2:]:
f_args.append(_prepare_ref(arg, for_names, file_args))
program.extend(self._parse_file(include_filename, for_names,
f_args))
else:
if len(args) != 2:
raise ArgCountSyntaxError()
program.append((self._cmd_include,
(_prepare_ref(args[1], for_names, file_args),
base)))
else:
# implied PRINT command
if len(args) > 1:
f_args = [ ]
for arg in args:
f_args.append(_prepare_ref(arg, for_names, file_args))
program.append((self._cmd_format, (f_args[0], f_args[1:])))
else:
program.append((self._cmd_print,
_prepare_ref(args[0], for_names, file_args)))
if stack:
### would be nice to say which blocks...
raise UnclosedBlocksError()
return program
def _execute(self, program, fp, ctx):
"""This private helper function takes a 'program' sequence as created
by the method '_parse' and executes it step by step. strings are written
to the file object 'fp' and functions are called.
"""
for step in program:
if isinstance(step, StringType):
fp.write(step)
else:
step[0](step[1], fp, ctx)
def _cmd_print(self, valref, fp, ctx):
value = _get_value(valref, ctx)
# if the value has a 'read' attribute, then it is a stream: copy it
if hasattr(value, 'read'):
while 1:
chunk = value.read(16384)
if not chunk:
break
fp.write(chunk)
else:
fp.write(value)
def _cmd_format(self, (valref, args), fp, ctx):
fmt = _get_value(valref, ctx)
parts = _re_subst.split(fmt)
for i in range(len(parts)):
piece = parts[i]
if i%2 == 1 and piece != '%':
idx = int(piece)
if idx < len(args):
piece = _get_value(args[idx], ctx)
else:
piece = '<undef>'
fp.write(piece)
def _cmd_include(self, (valref, base), fp, ctx):
fname = _get_value(valref, ctx)
if base:
fname = os.path.join(base, fname)
### note: we don't have the set of for_names to pass into this parse.
### I don't think there is anything to do but document it.
self._execute(self._parse_file(fname), fp, ctx)
def _cmd_if_any(self, args, fp, ctx):
"If the value is a non-empty string or non-empty list, then T else F."
((valref,), t_section, f_section) = args
value = _get_value(valref, ctx)
self._do_if(value, t_section, f_section, fp, ctx)
def _cmd_if_index(self, args, fp, ctx):
((valref, value), t_section, f_section) = args
list, idx = ctx.for_index[valref[0]]
if value == 'even':
value = idx % 2 == 0
elif value == 'odd':
value = idx % 2 == 1
elif value == 'first':
value = idx == 0
elif value == 'last':
value = idx == len(list)-1
else:
value = idx == int(value)
self._do_if(value, t_section, f_section, fp, ctx)
def _cmd_is(self, args, fp, ctx):
((left_ref, right_ref), t_section, f_section) = args
value = _get_value(right_ref, ctx)
value = string.lower(_get_value(left_ref, ctx)) == string.lower(value)
self._do_if(value, t_section, f_section, fp, ctx)
def _do_if(self, value, t_section, f_section, fp, ctx):
if t_section is None:
t_section = f_section
f_section = None
if value:
section = t_section
else:
section = f_section
if section is not None:
self._execute(section, fp, ctx)
def _cmd_for(self, args, fp, ctx):
((valref,), unused, section) = args
list = _get_value(valref, ctx)
if isinstance(list, StringType):
raise NeedSequenceError()
refname = valref[0]
ctx.for_index[refname] = idx = [ list, 0 ]
for item in list:
self._execute(section, fp, ctx)
idx[1] = idx[1] + 1
del ctx.for_index[refname]
def boolean(value):
"Return a value suitable for [if-any bool_var] usage in a template."
if value:
return 'yes'
return None
def _prepare_ref(refname, for_names, file_args):
"""refname -> a string containing a dotted identifier. example:"foo.bar.bang"
for_names -> a list of active for sequences.
Returns a `value reference', a 3-Tupel made out of (refname, start, rest),
for fast access later.
"""
# is the reference a string constant?
if refname[0] == '"':
return None, refname[1:-1], None
# if this is an include-argument, then just return the prepared ref
if refname[:3] == 'arg':
try:
idx = int(refname[3:])
except ValueError:
pass
else:
if idx < len(file_args):
return file_args[idx]
parts = string.split(refname, '.')
start = parts[0]
rest = parts[1:]
while rest and (start in for_names):
# check if the next part is also a "for name"
name = start + '.' + rest[0]
if name in for_names:
start = name
del rest[0]
else:
break
return refname, start, rest
def _get_value((refname, start, rest), ctx):
"""(refname, start, rest) -> a prepared `value reference' (see above).
ctx -> an execution context instance.
Does a name space lookup within the template name space. Active
for blocks take precedence over data dictionary members with the
same name.
"""
if rest is None:
# it was a string constant
return start
if ctx.for_index.has_key(start):
list, idx = ctx.for_index[start]
ob = list[idx]
elif ctx.data.has_key(start):
ob = ctx.data[start]
else:
raise UnknownReference(refname)
# walk the rest of the dotted reference
for attr in rest:
try:
ob = getattr(ob, attr)
except AttributeError:
raise UnknownReference(refname)
# make sure we return a string instead of some various Python types
if isinstance(ob, IntType) or isinstance(ob, FloatType):
return str(ob)
if ob is None:
return ''
# string or a sequence
return ob
class _context:
"""A container for the execution context"""
class ArgCountSyntaxError(Exception):
pass
class UnknownReference(Exception):
pass
class NeedSequenceError(Exception):
pass
class UnclosedBlocksError(Exception):
pass
# --- standard test environment ---
def test_parse():
assert _re_parse.split('[a]') == ['', '[a]', None, '']
assert _re_parse.split('[a] [b]') == \
['', '[a]', None, ' ', '[b]', None, '']
assert _re_parse.split('[a c] [b]') == \
['', '[a c]', None, ' ', '[b]', None, '']
assert _re_parse.split('x [a] y [b] z') == \
['x ', '[a]', None, ' y ', '[b]', None, ' z']
assert _re_parse.split('[a "b" c "d"]') == \
['', '[a "b" c "d"]', None, '']
assert _re_parse.split(r'["a \"b[foo]" c.d f]') == \
['', '["a \\"b[foo]" c.d f]', None, '']
def _test(argv):
import doctest, ezt
verbose = "-v" in argv
return doctest.testmod(ezt, verbose=verbose)
if __name__ == "__main__":
# invoke unit test for this module:
import sys
sys.exit(_test(sys.argv)[0])

347
lib/ndiff.py Normal file
View File

@@ -0,0 +1,347 @@
#! /usr/bin/env python
# Module ndiff version 1.6.0
# Released to the public domain 08-Dec-2000,
# by Tim Peters (tim.one@home.com).
# Backported to Python 1.5.2 for ViewCVS by pf@artcom-gmbh.de, 24-Dec-2001
# Provided as-is; use at your own risk; no warranty; no promises; enjoy!
"""ndiff [-q] file1 file2
or
ndiff (-r1 | -r2) < ndiff_output > file1_or_file2
Print a human-friendly file difference report to stdout. Both inter-
and intra-line differences are noted. In the second form, recreate file1
(-r1) or file2 (-r2) on stdout, from an ndiff report on stdin.
In the first form, if -q ("quiet") is not specified, the first two lines
of output are
-: file1
+: file2
Each remaining line begins with a two-letter code:
"- " line unique to file1
"+ " line unique to file2
" " line common to both files
"? " line not present in either input file
Lines beginning with "? " attempt to guide the eye to intraline
differences, and were not present in either input file. These lines can be
confusing if the source files contain tab characters.
The first file can be recovered by retaining only lines that begin with
" " or "- ", and deleting those 2-character prefixes; use ndiff with -r1.
The second file can be recovered similarly, but by retaining only " " and
"+ " lines; use ndiff with -r2; or, on Unix, the second file can be
recovered by piping the output through
sed -n '/^[+ ] /s/^..//p'
See module comments for details and programmatic interface.
"""
__version__ = 1, 6, 1
# SequenceMatcher tries to compute a "human-friendly diff" between
# two sequences (chiefly picturing a file as a sequence of lines,
# and a line as a sequence of characters, here). Unlike e.g. UNIX(tm)
# diff, the fundamental notion is the longest *contiguous* & junk-free
# matching subsequence. That's what catches peoples' eyes. The
# Windows(tm) windiff has another interesting notion, pairing up elements
# that appear uniquely in each sequence. That, and the method here,
# appear to yield more intuitive difference reports than does diff. This
# method appears to be the least vulnerable to synching up on blocks
# of "junk lines", though (like blank lines in ordinary text files,
# or maybe "<P>" lines in HTML files). That may be because this is
# the only method of the 3 that has a *concept* of "junk" <wink>.
#
# Note that ndiff makes no claim to produce a *minimal* diff. To the
# contrary, minimal diffs are often counter-intuitive, because they
# synch up anywhere possible, sometimes accidental matches 100 pages
# apart. Restricting synch points to contiguous matches preserves some
# notion of locality, at the occasional cost of producing a longer diff.
#
# With respect to junk, an earlier version of ndiff simply refused to
# *start* a match with a junk element. The result was cases like this:
# before: private Thread currentThread;
# after: private volatile Thread currentThread;
# If you consider whitespace to be junk, the longest contiguous match
# not starting with junk is "e Thread currentThread". So ndiff reported
# that "e volatil" was inserted between the 't' and the 'e' in "private".
# While an accurate view, to people that's absurd. The current version
# looks for matching blocks that are entirely junk-free, then extends the
# longest one of those as far as possible but only with matching junk.
# So now "currentThread" is matched, then extended to suck up the
# preceding blank; then "private" is matched, and extended to suck up the
# following blank; then "Thread" is matched; and finally ndiff reports
# that "volatile " was inserted before "Thread". The only quibble
# remaining is that perhaps it was really the case that " volatile"
# was inserted after "private". I can live with that <wink>.
#
# NOTE on junk: the module-level names
# IS_LINE_JUNK
# IS_CHARACTER_JUNK
# can be set to any functions you like. The first one should accept
# a single string argument, and return true iff the string is junk.
# The default is whether the regexp r"\s*#?\s*$" matches (i.e., a
# line without visible characters, except for at most one splat).
# The second should accept a string of length 1 etc. The default is
# whether the character is a blank or tab (note: bad idea to include
# newline in this!).
#
# After setting those, you can call fcompare(f1name, f2name) with the
# names of the files you want to compare. The difference report
# is sent to stdout. Or you can call main(args), passing what would
# have been in sys.argv[1:] had the cmd-line form been used.
from difflib import SequenceMatcher
import string
TRACE = 0
# define what "junk" means
import re
def IS_LINE_JUNK(line, pat=re.compile(r"\s*#?\s*$").match):
return pat(line) is not None
def IS_CHARACTER_JUNK(ch, ws=" \t"):
return ch in ws
del re
# meant for dumping lines
def dump(tag, x, lo, hi):
for i in xrange(lo, hi):
print tag, x[i],
def plain_replace(a, alo, ahi, b, blo, bhi):
assert alo < ahi and blo < bhi
# dump the shorter block first -- reduces the burden on short-term
# memory if the blocks are of very different sizes
if bhi - blo < ahi - alo:
dump('+', b, blo, bhi)
dump('-', a, alo, ahi)
else:
dump('-', a, alo, ahi)
dump('+', b, blo, bhi)
# When replacing one block of lines with another, this guy searches
# the blocks for *similar* lines; the best-matching pair (if any) is
# used as a synch point, and intraline difference marking is done on
# the similar pair. Lots of work, but often worth it.
def fancy_replace(a, alo, ahi, b, blo, bhi):
if TRACE:
print '*** fancy_replace', alo, ahi, blo, bhi
dump('>', a, alo, ahi)
dump('<', b, blo, bhi)
# don't synch up unless the lines have a similarity score of at
# least cutoff; best_ratio tracks the best score seen so far
best_ratio, cutoff = 0.74, 0.75
cruncher = SequenceMatcher(IS_CHARACTER_JUNK)
eqi, eqj = None, None # 1st indices of equal lines (if any)
# search for the pair that matches best without being identical
# (identical lines must be junk lines, & we don't want to synch up
# on junk -- unless we have to)
for j in xrange(blo, bhi):
bj = b[j]
cruncher.set_seq2(bj)
for i in xrange(alo, ahi):
ai = a[i]
if ai == bj:
if eqi is None:
eqi, eqj = i, j
continue
cruncher.set_seq1(ai)
# computing similarity is expensive, so use the quick
# upper bounds first -- have seen this speed up messy
# compares by a factor of 3.
# note that ratio() is only expensive to compute the first
# time it's called on a sequence pair; the expensive part
# of the computation is cached by cruncher
if cruncher.real_quick_ratio() > best_ratio and \
cruncher.quick_ratio() > best_ratio and \
cruncher.ratio() > best_ratio:
best_ratio, best_i, best_j = cruncher.ratio(), i, j
if best_ratio < cutoff:
# no non-identical "pretty close" pair
if eqi is None:
# no identical pair either -- treat it as a straight replace
plain_replace(a, alo, ahi, b, blo, bhi)
return
# no close pair, but an identical pair -- synch up on that
best_i, best_j, best_ratio = eqi, eqj, 1.0
else:
# there's a close pair, so forget the identical pair (if any)
eqi = None
# a[best_i] very similar to b[best_j]; eqi is None iff they're not
# identical
if TRACE:
print '*** best_ratio', best_ratio, best_i, best_j
dump('>', a, best_i, best_i+1)
dump('<', b, best_j, best_j+1)
# pump out diffs from before the synch point
fancy_helper(a, alo, best_i, b, blo, best_j)
# do intraline marking on the synch pair
aelt, belt = a[best_i], b[best_j]
if eqi is None:
# pump out a '-', '?', '+', '?' quad for the synched lines
atags = btags = ""
cruncher.set_seqs(aelt, belt)
for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
la, lb = ai2 - ai1, bj2 - bj1
if tag == 'replace':
atags = atags + '^' * la
btags = btags + '^' * lb
elif tag == 'delete':
atags = atags + '-' * la
elif tag == 'insert':
btags = btags + '+' * lb
elif tag == 'equal':
atags = atags + ' ' * la
btags = btags + ' ' * lb
else:
raise ValueError, 'unknown tag ' + `tag`
printq(aelt, belt, atags, btags)
else:
# the synch pair is identical
print ' ', aelt,
# pump out diffs from after the synch point
fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
def fancy_helper(a, alo, ahi, b, blo, bhi):
if alo < ahi:
if blo < bhi:
fancy_replace(a, alo, ahi, b, blo, bhi)
else:
dump('-', a, alo, ahi)
elif blo < bhi:
dump('+', b, blo, bhi)
# Crap to deal with leading tabs in "?" output. Can hurt, but will
# probably help most of the time.
def printq(aline, bline, atags, btags):
common = min(count_leading(aline, "\t"),
count_leading(bline, "\t"))
common = min(common, count_leading(atags[:common], " "))
print "-", aline,
if count_leading(atags, " ") < len(atags):
print "?", "\t" * common + atags[common:]
print "+", bline,
if count_leading(btags, " ") < len(btags):
print "?", "\t" * common + btags[common:]
def count_leading(line, ch):
i, n = 0, len(line)
while i < n and line[i] == ch:
i = i+1
return i
def fail(msg):
import sys
out = sys.stderr.write
out(msg + "\n\n")
out(__doc__)
return 0
# open a file & return the file object; gripe and return 0 if it
# couldn't be opened
def fopen(fname):
try:
return open(fname, 'r')
except IOError, detail:
return fail("couldn't open " + fname + ": " + str(detail))
# open two files & spray the diff to stdout; return false iff a problem
def fcompare(f1name, f2name):
f1 = fopen(f1name)
f2 = fopen(f2name)
if not f1 or not f2:
return 0
a = f1.readlines(); f1.close()
b = f2.readlines(); f2.close()
cruncher = SequenceMatcher(IS_LINE_JUNK, a, b)
for tag, alo, ahi, blo, bhi in cruncher.get_opcodes():
if tag == 'replace':
fancy_replace(a, alo, ahi, b, blo, bhi)
elif tag == 'delete':
dump('-', a, alo, ahi)
elif tag == 'insert':
dump('+', b, blo, bhi)
elif tag == 'equal':
dump(' ', a, alo, ahi)
else:
raise ValueError, 'unknown tag ' + `tag`
return 1
# crack args (sys.argv[1:] is normal) & compare;
# return false iff a problem
def main(args):
import getopt
try:
opts, args = getopt.getopt(args, "qr:")
except getopt.error, detail:
return fail(str(detail))
noisy = 1
qseen = rseen = 0
for opt, val in opts:
if opt == "-q":
qseen = 1
noisy = 0
elif opt == "-r":
rseen = 1
whichfile = val
if qseen and rseen:
return fail("can't specify both -q and -r")
if rseen:
if args:
return fail("no args allowed with -r option")
if whichfile in "12":
restore(whichfile)
return 1
return fail("-r value must be 1 or 2")
if len(args) != 2:
return fail("need 2 filename args")
f1name, f2name = args
if noisy:
print '-:', f1name
print '+:', f2name
return fcompare(f1name, f2name)
def restore(which):
import sys
tag = {"1": "- ", "2": "+ "}[which]
prefixes = (" ", tag)
for line in sys.stdin.readlines():
if line[:2] in prefixes:
print line[2:],
if __name__ == '__main__':
import sys
args = sys.argv[1:]
if "-profile" in args:
import profile, pstats
args.remove("-profile")
statf = "ndiff.pro"
profile.run("main(args)", statf)
stats = pstats.Stats(statf)
stats.strip_dirs().sort_stats('time').print_stats()
else:
main(args)

190
lib/popen.py Normal file
View File

@@ -0,0 +1,190 @@
#
# Copyright (C) 2000-2001 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 ViewCVS
# distribution or at http://viewcvs.sourceforge.net/license-1.html.
#
# Contact information:
# Greg Stein, PO Box 760, Palo Alto, CA, 94302
# gstein@lyra.org, http://viewcvs.sourceforge.net/
#
# -----------------------------------------------------------------------
#
# popen.py: a replacement for os.popen()
#
# This implementation of popen() provides a cmd + args calling sequence,
# rather than a system() type of convention. The shell facilities are not
# available, but that implies we can avoid worrying about shell hacks in
# the arguments.
#
# -----------------------------------------------------------------------
#
import os
import sys
def popen(cmd, args, mode, capture_err=1):
# flush the stdio buffers since we are about to change the FD under them
sys.stdout.flush()
sys.stderr.flush()
r, w = os.pipe()
pid = os.fork()
if pid:
# in the parent
# close the descriptor that we don't need and return the other one.
if mode == 'r':
os.close(w)
return _pipe(os.fdopen(r, 'r'), pid)
os.close(r)
return _pipe(os.fdopen(w, 'w'), pid)
# in the child
# we'll need /dev/null for the discarded I/O
null = os.open('/dev/null', os.O_RDWR)
if mode == 'r':
# hook stdout/stderr to the "write" channel
os.dup2(w, 1)
# "close" stdin; the child shouldn't use it
### this isn't quite right... we may want the child to read from stdin
os.dup2(null, 0)
# what to do with errors?
if capture_err:
os.dup2(w, 2)
else:
os.dup2(null, 2)
else:
# hook stdin to the "read" channel
os.dup2(r, 0)
# "close" stdout/stderr; the child shouldn't use them
### this isn't quite right... we may want the child to write to these
os.dup2(null, 1)
os.dup2(null, 2)
# don't need these FDs any more
os.close(null)
os.close(r)
os.close(w)
# the stdin/stdout/stderr are all set up. exec the target
try:
os.execvp(cmd, (cmd,) + tuple(args))
except:
# aid debugging, if the os.execvp above fails for some reason:
import string
print "<h2>exec failed:</h2><pre>", cmd, string.join(args), "</pre>"
raise
# crap. shouldn't be here.
sys.exit(127)
def pipe_cmds(cmds):
# flush the stdio buffers since we are about to change the FD under them
sys.stdout.flush()
sys.stderr.flush()
prev_r, parent_w = os.pipe()
null = os.open('/dev/null', os.O_RDWR)
for cmd in cmds[:-1]:
r, w = os.pipe()
pid = os.fork()
if not pid:
# in the child
# hook up stdin to the "read" channel
os.dup2(prev_r, 0)
# hook up stdout to the output channel
os.dup2(w, 1)
# toss errors
os.dup2(null, 2)
# close these extra descriptors
os.close(prev_r)
os.close(parent_w)
os.close(null)
os.close(r)
os.close(w)
# time to run the command
try:
os.execvp(cmd[0], cmd)
except:
pass
sys.exit(127)
# in the parent
# we don't need these any more
os.close(prev_r)
os.close(w)
# the read channel of this pipe will feed into to the next command
prev_r = r
# no longer needed
os.close(null)
# done with most of the commands. set up the last command to write to stdout
pid = os.fork()
if not pid:
# in the child (the last command)
# hook up stdin to the "read" channel
os.dup2(prev_r, 0)
# close these extra descriptors
os.close(prev_r)
os.close(parent_w)
# run the last command
try:
os.execvp(cmds[-1][0], cmds[-1])
except:
pass
sys.exit(127)
# not needed any more
os.close(prev_r)
# write into the first pipe, wait on the final process
return _pipe(os.fdopen(parent_w, 'w'), pid)
class _pipe:
"Wrapper for a file which can wait() on a child process at close time."
def __init__(self, file, child_pid):
self.file = file
self.child_pid = child_pid
def eof(self):
pid, status = os.waitpid(self.child_pid, os.WNOHANG)
if pid:
self.file.close()
self.file = None
return status
return None
def close(self):
if self.file:
self.file.close()
self.file = None
return os.waitpid(self.child_pid, 0)[1]
return None
def __getattr__(self, name):
return getattr(self.file, name)
def __del__(self):
if self.file:
self.close()

489
lib/py2html.py Normal file
View File

@@ -0,0 +1,489 @@
#!/usr/local/bin/python -u
""" Python Highlighter for HTML Version: 0.5
py2html.py [options] files...
options:
-h print help
- read from stdin, write to stdout
-stdout read from files, write to stdout
-files read from files, write to filename+'.html' (default)
-format:
html output HTML page (default)
rawhtml output pure HTML (without headers, titles, etc.)
-mode:
color output in color (default)
mono output b/w (for printing)
-title:Title use 'Title' as title of the generated page
-bgcolor:color use color as background-color for page
-header:file use contents of file as header
-footer:file use contents of file as footer
-URL replace all occurances of 'URL: link' with
'<A HREF="link">link</A>'; this is always enabled
in CGI mode
-v verbose
Takes the input, assuming it is Python code and formats it into
colored HTML. When called without parameters the script tries to
work in CGI mode. It looks for a field 'script=URL' and tries to
use that URL as input file. If it can't find this field, the path
info (the part of the URL following the CGI script name) is
tried. In case no host is given, the host where the CGI script
lives and HTTP are used.
* Uses Just van Rossum's PyFontify version 0.3 to tag Python scripts.
You can get it via his homepage on starship:
URL: http://starship.skyport.net/crew/just
"""
__comments__ = """
The following snippet is a small shell script I use for viewing
Python scripts per less on Unix:
#!/bin/sh
# Browse pretty printed Python code using ANSI codes for highlighting
py2html -stdout -format:ansi -mode:mono $* | less -r
History:
0.5: Added a few suggestions by Kevin Ng to make the CGI version
a little more robust.
"""
__copyright__ = """
-----------------------------------------------------------------------------
(c) Copyright by Marc-Andre Lemburg, 1998 (mailto:mal@lemburg.com)
Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee or royalty is hereby granted,
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in
supporting documentation or portions thereof, including modifications,
that you make.
THE AUTHOR MARC-ANDRE LEMBURG DISCLAIMS ALL WARRANTIES WITH REGARD TO
THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS, IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL,
INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
WITH THE USE OR PERFORMANCE OF THIS SOFTWARE !
"""
__version__ = '0.5'
__cgifooter__ = ('\n<PRE># code highlighted using <A HREF='
'"http://starship.skyport.net/~lemburg/">py2html.py</A> '
'version %s</PRE>\n' % __version__)
import sys,string,re
# Adjust path so that PyFontify is found...
sys.path.append('.')
### Constants
# URL of the input form the user is redirected to in case no script=xxx
# form field is given. The URL *must* be absolute. Leave blank to
# have the script issue an error instead.
INPUT_FORM = 'http://starship.skyport.net/~lemburg/SoftwareDescriptions.html#py2html.py'
### Helpers
def fileio(file, mode='rb', data=None, close=0):
if type(file) == type(''):
f = open(file,mode)
close = 1
else:
f = file
if data:
f.write(data)
else:
data = f.read()
if close: f.close()
return data
### Converter class
class PrettyPrint:
""" generic Pretty Printer class
* supports tagging Python scripts in the following ways:
# format/mode | color mono
# --------------------------
# rawhtml | x x (HTML without headers, etc.)
# html | x x (a HTML page with HEAD&BODY:)
# ansi | x (with Ansi-escape sequences)
* interfaces:
file_filter -- takes two files: input & output (may be stdin/stdout)
filter -- takes a string and returns the highlighted version
* to create an instance use:
c = PrettyPrint(tagfct,format,mode)
where format and mode must be strings according to the
above table if you plan to use PyFontify.fontify as
tagfct
* the tagfct has to take one argument, text, and return a taglist
(format: [(id,left,right,sublist),...], where id is the
"name" given to the slice left:right in text and sublist is a
taglist for tags inside the slice or None)
"""
# misc settings
title = ''
bgcolor = '#FFFFFF'
header = ''
footer = ''
replace_URLs = 0
# formats to be used
formats = {}
def __init__(self,tagfct=None,format='html',mode='color'):
self.tag = tagfct
self.set_mode = getattr(self,'set_mode_'+format+'_'+mode)
self.filter = getattr(self,'filter_'+format)
self.set_mode()
def file_filter(self,infile,outfile):
text = fileio(infile,'r')
if type(infile) == type('') and self.title == '':
self.title = infile
fileio(outfile,'w',self.filter(text))
### set pre- and postfixes for formats & modes
#
# These methods must set self.formats to a dictionary having
# an entry for every tag returned by the tagging function.
#
# The format used is simple:
# tag:(prefix,postfix)
# where prefix and postfix are either strings or callable objects,
# that return a string (they are called with the matching tag text
# as only parameter). prefix is inserted in front of the tag, postfix
# is inserted right after the tag.
def set_mode_html_color(self):
self.formats = {
'all':('<PRE>','</PRE>'),
'comment':('<FONT COLOR=#1111CC>','</FONT>'),
'keyword':('<FONT COLOR=#3333CC><B>','</B></FONT>'),
'parameter':('<FONT COLOR=#000066>','</FONT>'),
'identifier':( lambda x,strip=string.strip:
'<A NAME="%s"><FONT COLOR=#CC0000><B>' % (strip(x)),
'</B></FONT></A>'),
'string':('<FONT COLOR=#115511>','</FONT>')
}
set_mode_rawhtml_color = set_mode_html_color
def set_mode_html_mono(self):
self.formats = {
'all':('<PRE>','</PRE>'),
'comment':('',''),
'keyword':( '<U>','</U>'),
'parameter':('',''),
'identifier':( lambda x,strip=string.strip:
'<A NAME="%s"><B>' % (strip(x)),
'</B>'),
'string':('','')
}
set_mode_rawhtml_mono = set_mode_html_mono
def set_mode_ansi_mono(self):
self.formats = {
'all':('',''),
'comment':('\033[2m','\033[m'),
'keyword':('\033[4m','\033[m'),
'parameter':('',''),
'identifier':('\033[1m','\033[m'),
'string':('','')
}
### filter for Python scripts given as string
def escape_html(self,text):
t = (('<','&lt;'),('>','&gt;'))
for x,y in t:
text = string.join(string.split(text,x),y)
return text
def filter_html(self,text):
output = self.fontify(self.escape_html(text))
if self.replace_URLs:
output = re.sub('URL:([ \t]+)([^ \n\r<]+)',
'URL:\\1<A HREF="\\2">\\2</A>',output)
html = """<HTML><HEAD><TITLE>%s</TITLE></HEAD>
<BODY BGCOLOR=%s>
<!--header-->%s
<!--script-->%s
<!--footer-->%s
</BODY>\n"""%(self.title,self.bgcolor,self.header,output,self.footer)
return html
def filter_rawhtml(self,text):
output = self.fontify(self.escape_html(text))
if self.replace_URLs:
output = re.sub('URL:([ \t]+)([^ \n\r<]+)',
'URL:\\1<A HREF="\\2">\\2</A>',output)
return self.header+output+self.footer
def filter_ansi(self,text):
output = self.fontify(text)
return self.header+output+self.footer
### fontify engine
def fontify(self,pytext):
# parse
taglist = self.tag(pytext)
# prepend special 'all' tag:
taglist[:0] = [('all',0,len(pytext),None)]
# prepare splitting
splits = []
addsplits(splits,pytext,self.formats,taglist)
# do splitting & inserting
splits.sort()
l = []
li = 0
for ri,dummy,insert in splits:
if ri > li: l.append(pytext[li:ri])
l.append(insert)
li = ri
if li < len(pytext): l.append(pytext[li:])
return string.join(l,'')
def addsplits(splits,text,formats,taglist):
# helper for fontify()
for id,left,right,sublist in taglist:
try:
pre,post = formats[id]
except KeyError:
# sys.stderr.write('Warning: no format for %s specified\n'%repr(id))
pre,post = '',''
if type(pre) != type(''):
pre = pre(text[left:right])
if type(post) != type(''):
post = post(text[left:right])
# len(splits) is a dummy used to make sorting stable
splits.append((left,len(splits),pre))
if sublist:
addsplits(splits,text,formats,sublist)
splits.append((right,len(splits),post))
def write_html_error(titel,text):
print """\
<HTML><HEADER><TITLE>%s</TITLE></HEADER>
<BODY>
<H2>%s</H2>
%s
</BODY></HTML>
""" % (titel,titel,text)
def redirect_to(url):
sys.stdout.write('Content-Type: text/html\r\n')
sys.stdout.write('Status: 302\r\n')
sys.stdout.write('Location: %s\r\n\r\n' % url)
print """
<HTML><HEAD>
<TITLE>302 Moved Temporarily</TITLE>
</HEAD><BODY>
<H1>302 Moved Temporarily</H1>
The document has moved to <A HREF="%s">%s</A>.<P>
</BODY></HTML>
""" % (url,url)
def main(cmdline):
""" main(cmdline) -- process cmdline as if it were sys.argv
"""
# parse options/files
options = []
optvalues = {}
for o in cmdline[1:]:
if o[0] == '-':
if ':' in o:
k,v = tuple(string.split(o,':'))
optvalues[k] = v
options.append(k)
else:
options.append(o)
else:
break
files = cmdline[len(options)+1:]
# create converting object
# load fontifier
if '-marcs' in options:
# use mxTextTool's tagging engine
from mxTextTools import tag
from mxTextTools.Examples.Python import python_script
tagfct = lambda text,tag=tag,pytable=python_script: \
tag(text,pytable)[1]
print "Py2HTML: using Marc's tagging engine"
else:
# load Just's
try:
import PyFontify
if PyFontify.__version__ < '0.3': raise ValueError
tagfct = PyFontify.fontify
except:
print """
Sorry, but this script needs the PyFontify.py module version 0.3;
You can download it from Just's homepage at
URL: http://starship.skyport.net/crew/just
"""
sys.exit()
if '-format' in options:
format = optvalues['-format']
else:
# use default
format = 'html'
if '-mode' in options:
mode = optvalues['-mode']
else:
# use default
mode = 'color'
c = PrettyPrint(tagfct,format,mode)
convert = c.file_filter
# start working
if '-title' in options:
c.title = optvalues['-title']
if '-bgcolor' in options:
c.bgcolor = optvalues['-bgcolor']
if '-header' in options:
try:
f = open(optvalues['-header'])
c.header = f.read()
f.close()
except IOError:
if verbose: print 'IOError: header file not found'
if '-footer' in options:
try:
f = open(optvalues['-footer'])
c.footer = f.read()
f.close()
except IOError:
if verbose: print 'IOError: footer file not found'
if '-URL' in options:
c.replace_URLs = 1
if '-' in options:
convert(sys.stdin,sys.stdout)
sys.exit()
if '-h' in options:
print __doc__
sys.exit()
if len(files) == 0:
# Turn URL processing on
c.replace_URLs = 1
# Try CGI processing...
import cgi,urllib,urlparse,os
form = cgi.FieldStorage()
if not form.has_key('script'):
# Ok, then try pathinfo
if not os.environ.has_key('PATH_INFO'):
if INPUT_FORM:
redirect_to(INPUT_FORM)
else:
sys.stdout.write('Content-Type: text/html\r\n\r\n')
write_html_error('Missing Parameter',
'Missing script=URL field in request')
sys.exit(1)
url = os.environ['PATH_INFO'][1:] # skip the leading slash
else:
url = form['script'].value
sys.stdout.write('Content-Type: text/html\r\n\r\n')
scheme, host, path, params, query, frag = urlparse.urlparse(url)
if not host:
scheme = 'http'
if os.environ.has_key('HTTP_HOST'):
host = os.environ['HTTP_HOST']
else:
host = 'localhost'
url = urlparse.urlunparse((scheme, host, path, params, query, frag))
#print url; sys.exit()
network = urllib.URLopener()
try:
tempfile,headers = network.retrieve(url)
except IOError,reason:
write_html_error('Error opening "%s"' % url,
'The given URL could not be opened. Reason: %s' %\
str(reason))
sys.exit(1)
f = open(tempfile,'rb')
c.title = url
c.footer = __cgifooter__
convert(f,sys.stdout)
f.close()
network.close()
sys.exit()
if '-stdout' in options:
filebreak = '-'*72
for f in files:
try:
if len(files) > 1:
print filebreak
print 'File:',f
print filebreak
convert(f,sys.stdout)
except IOError:
pass
else:
verbose = ('-v' in options)
if verbose:
print 'Py2HTML: working on',
for f in files:
try:
if verbose: print f,
convert(f,f+'.html')
except IOError:
if verbose: print '(IOError!)',
if verbose:
print
print 'Done.'
if __name__=='__main__':
main(sys.argv)

437
lib/query.py Normal file
View File

@@ -0,0 +1,437 @@
#!/usr/bin/python
# -*- Mode: python -*-
#
# Copyright (C) 2000 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 ViewCVS
# distribution or at http://www.lyra.org/viewcvs/license-1.html.
#
# Contact information:
# Greg Stein, PO Box 760, Palo Alto, CA, 94302
# gstein@lyra.org, http://www.lyra.org/viewcvs/
#
# -----------------------------------------------------------------------
#
# CGI script to process and display queries to CVSdb
#
# This script is part of the ViewCVS package. More information can be
# found at http://www.lyra.org/viewcvs/.
#
# -----------------------------------------------------------------------
#
#########################################################################
#
# INSTALL-TIME CONFIGURATION
#
# These values will be set during the installation process. During
# development, they will remain None.
#
CONF_PATHNAME = None
#########################################################################
import os
import sys
import string
import cgi
import time
import cvsdb
import viewcvs
import ezt
class FormData:
def __init__(self, form):
self.valid = 0
self.repository = ""
self.branch = ""
self.directory = ""
self.file = ""
self.who = ""
self.sortby = ""
self.date = ""
self.hours = 0
self.decode_thyself(form)
def decode_thyself(self, form):
try:
self.repository = string.strip(form["repository"].value)
except KeyError:
pass
except TypeError:
pass
else:
self.valid = 1
try:
self.branch = string.strip(form["branch"].value)
except KeyError:
pass
except TypeError:
pass
else:
self.valid = 1
try:
self.directory = string.strip(form["directory"].value)
except KeyError:
pass
except TypeError:
pass
else:
self.valid = 1
try:
self.file = string.strip(form["file"].value)
except KeyError:
pass
except TypeError:
pass
else:
self.valid = 1
try:
self.who = string.strip(form["who"].value)
except KeyError:
pass
except TypeError:
pass
else:
self.valid = 1
try:
self.sortby = string.strip(form["sortby"].value)
except KeyError:
pass
except TypeError:
pass
try:
self.date = string.strip(form["date"].value)
except KeyError:
pass
except TypeError:
pass
try:
self.hours = int(form["hours"].value)
except KeyError:
pass
except TypeError:
pass
except ValueError:
pass
else:
self.valid = 1
## returns a tuple-list (mod-str, string)
def listparse_string(str):
return_list = []
cmd = ""
temp = ""
escaped = 0
state = "eat leading whitespace"
for c in str:
## handle escaped charactors
if not escaped and c == "\\":
escaped = 1
continue
## strip leading white space
if state == "eat leading whitespace":
if c in string.whitespace:
continue
else:
state = "get command or data"
## parse to '"' or ","
if state == "get command or data":
## just add escaped charactors
if escaped:
escaped = 0
temp = temp + c
continue
## the data is in quotes after the command
elif c == "\"":
cmd = temp
temp = ""
state = "get quoted data"
continue
## this tells us there was no quoted data, therefore no
## command; add the command and start over
elif c == ",":
## strip ending whitespace on un-quoted data
temp = string.rstrip(temp)
return_list.append( ("", temp) )
temp = ""
state = "eat leading whitespace"
continue
## record the data
else:
temp = temp + c
continue
## parse until ending '"'
if state == "get quoted data":
## just add escaped charactors
if escaped:
escaped = 0
temp = temp + c
continue
## look for ending '"'
elif c == "\"":
return_list.append( (cmd, temp) )
cmd = ""
temp = ""
state = "eat comma after quotes"
continue
## record the data
else:
temp = temp + c
continue
## parse until ","
if state == "eat comma after quotes":
if c in string.whitespace:
continue
elif c == ",":
state = "eat leading whitespace"
continue
else:
print "format error"
sys.exit(1)
if cmd or temp:
return_list.append((cmd, temp))
return return_list
def decode_command(cmd):
if cmd == "r":
return "regex"
elif cmd == "l":
return "like"
else:
return "exact"
def form_to_cvsdb_query(form_data):
query = cvsdb.CreateCheckinQuery()
if form_data.repository:
for cmd, str in listparse_string(form_data.repository):
cmd = decode_command(cmd)
query.SetRepository(str, cmd)
if form_data.branch:
for cmd, str in listparse_string(form_data.branch):
cmd = decode_command(cmd)
query.SetBranch(str, cmd)
if form_data.directory:
for cmd, str in listparse_string(form_data.directory):
cmd = decode_command(cmd)
query.SetDirectory(str, cmd)
if form_data.file:
for cmd, str in listparse_string(form_data.file):
cmd = decode_command(cmd)
query.SetFile(str, cmd)
if form_data.who:
for cmd, str in listparse_string(form_data.who):
cmd = decode_command(cmd)
query.SetAuthor(str, cmd)
if form_data.sortby == "author":
query.SetSortMethod("author")
elif form_data.sortby == "file":
query.SetSortMethod("file")
else:
query.SetSortMethod("date")
if form_data.date:
if form_data.date == "hours" and form_data.hours:
query.SetFromDateHoursAgo(form_data.hours)
elif form_data.date == "day":
query.SetFromDateDaysAgo(1)
elif form_data.date == "week":
query.SetFromDateDaysAgo(7)
elif form_data.date == "month":
query.SetFromDateDaysAgo(31)
return query
def cvsroot_name_from_path(cvsroot):
## we need to resolve the cvsroot path from the database
## to the name given to it in the viewcvs.conf file
for key, value in cfg.general.cvs_roots.items():
if value == cvsroot:
return key
return None
def build_commit(desc, files):
ob = _item(num_files=len(files), files=[])
if desc:
ob.desc = string.replace(cgi.escape(desc), '\n', '<br>')
else:
ob.desc = '&nbsp;'
for commit in files:
dir_parts = filter(None, string.split(commit.GetDirectory(), '/'))
if dir_parts \
and ((dir_parts[0] == 'CVSROOT' and cfg.options.hide_cvsroot) \
or cfg.is_forbidden(dir_parts[0])):
continue
ctime = commit.GetTime()
if not ctime:
ctime = "&nbsp";
else:
ctime = time.strftime("%y/%m/%d %H:%M", time.localtime(ctime))
## make the file link
file = os.path.join(commit.GetDirectory(), commit.GetFile())
file_full_path = os.path.join(commit.GetRepository(), file)
## if we couldn't find the cvsroot path configured in the
## viewcvs.conf file, then don't make the link
cvsroot_name = cvsroot_name_from_path(commit.GetRepository())
if cvsroot_name:
flink = '<a href="viewcvs.cgi/%s?cvsroot=%s">%s</a>' \
% (file, cvsroot_name, file_full_path)
else:
flink = file_full_path
ob.files.append(_item(date=ctime,
author=commit.GetAuthor(),
link=flink,
rev=commit.GetRevision(),
branch=commit.GetBranch(),
plus=int(commit.GetPlusCount()),
minus=int(commit.GetMinusCount()),
))
return ob
def run_query(form_data):
query = form_to_cvsdb_query(form_data)
db = cvsdb.ConnectDatabaseReadOnly()
db.RunQuery(query)
if not query.commit_list:
return [ ]
commits = [ ]
files = [ ]
current_desc = query.commit_list[0].GetDescription()
for commit in query.commit_list:
desc = commit.GetDescription()
if current_desc == desc:
files.append(commit)
continue
commits.append(build_commit(current_desc, files))
files = [ commit ]
current_desc = desc
## add the last file group to the commit list
commits.append(build_commit(current_desc, files))
return commits
def handle_config():
viewcvs.handle_config()
global cfg
cfg = viewcvs.cfg
def main():
handle_config()
form = cgi.FieldStorage()
form_data = FormData(form)
if form_data.valid:
commits = run_query(form_data)
query = None
else:
commits = [ ]
query = 'skipped'
data = {
'cfg' : cfg,
'address' : cfg.general.address,
'vsn' : viewcvs.__version__,
'repository' : cgi.escape(form_data.repository, 1),
'branch' : cgi.escape(form_data.branch, 1),
'directory' : cgi.escape(form_data.directory, 1),
'file' : cgi.escape(form_data.file, 1),
'who' : cgi.escape(form_data.who, 1),
'sortby' : form_data.sortby,
'date' : form_data.date,
'query' : query,
'commits' : commits,
'num_commits' : len(commits),
}
if form_data.hours:
data['hours'] = form_data.hours
else:
data['hours'] = 2
template = ezt.Template()
template.parse_file(os.path.join(viewcvs.g_install_dir,
cfg.templates.query))
viewcvs.http_header()
# generate the page
template.generate(sys.stdout, data)
def run_cgi():
### be nice to share all this logic with viewcvs.run_cgi
try:
main()
except SystemExit, e:
# don't catch SystemExit (caused by sys.exit()). propagate the exit code
sys.exit(e[0])
except:
info = sys.exc_info()
viewcvs.http_header()
print '<html><head><title>Python Exception Occurred</title></head>'
print '<body bgcolor=white><h1>Python Exception Occurred</h1>'
import traceback
lines = apply(traceback.format_exception, info)
print '<pre>'
print cgi.escape(string.join(lines, ''))
print '</pre>'
viewcvs.html_footer()
class _item:
def __init__(self, **kw):
vars(self).update(kw)

441
lib/rcsparse.py Normal file
View File

@@ -0,0 +1,441 @@
#
# Copyright (C) 2000-2001 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 ViewCVS
# distribution or at http://viewcvs.sourceforge.net/license-1.html.
#
# Contact information:
# Greg Stein, PO Box 760, Palo Alto, CA, 94302
# gstein@lyra.org, http://viewcvs.sourceforge.net/
#
# -----------------------------------------------------------------------
#
# This software is being maintained as part of the ViewCVS project.
# Information is available at:
# http://viewcvs.sourceforge.net/
#
# This file was originally based on portions of the blame.py script by
# Curt Hagenlocher.
#
# -----------------------------------------------------------------------
import string
import time
class _TokenStream:
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.
CHUNK_SIZE = 100000
# CHUNK_SIZE = 5 # for debugging, make the function grind...
def __init__(self, file):
self.rcsfile = file
self.idx = 0
self.buf = self.rcsfile.read(self.CHUNK_SIZE)
if self.buf == '':
raise RuntimeError, 'EOF'
def get(self):
"Get the next token from the RCS file."
# Note: we can afford to loop within Python, examining individual
# characters. For the whitespace and tokens, the number of iterations
# is typically quite small. Thus, a simple iterative loop will beat
# out more complex solutions.
buf = self.buf
idx = self.idx
while 1:
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
idx = 0
if buf[idx] not in string.whitespace:
break
idx = idx + 1
if buf[idx] == ';':
self.buf = buf
self.idx = idx + 1
return ';'
if buf[idx] != '@':
end = idx + 1
token = ''
while 1:
# find token characters in the current buffer
while end < len(buf) and buf[end] not in self.token_term:
end = end + 1
token = token + buf[idx:end]
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)
idx = end = 0
self.buf = buf
self.idx = idx
return token
# a "string" which starts with the "@" character. we'll skip it when we
# search for content.
idx = idx + 1
chunks = [ ]
while 1:
if idx == len(buf):
idx = 0
buf = self.rcsfile.read(self.CHUNK_SIZE)
if buf == '':
raise RuntimeError, 'EOF'
i = string.find(buf, '@', idx)
if i == -1:
chunks.append(buf[idx:])
idx = len(buf)
continue
if i == len(buf) - 1:
chunks.append(buf[idx:i])
idx = 0
buf = '@' + self.rcsfile.read(self.CHUNK_SIZE)
if buf == '@':
raise RuntimeError, 'EOF'
continue
if buf[i + 1] == '@':
chunks.append(buf[idx:i+1])
idx = i + 2
continue
chunks.append(buf[idx:i])
self.buf = buf
self.idx = i + 1
return string.join(chunks, '')
# _get = get
# def get(self):
token = self._get()
print 'T:', `token`
return token
def match(self, match):
"Try to match the next token from the input buffer."
token = self.get()
if token != match:
raise RuntimeError, ('Unexpected parsing error in RCS file.\n' +
'Expected token: %s, but saw: %s' % (match, token))
def unget(self, token):
"Put this token back, for the next get() to return."
# Override the class' .get method with a function which clears the
# overridden method then returns the pushed token. Since this function
# will not be looked up via the class mechanism, it should be a "normal"
# function, meaning it won't have "self" automatically inserted.
# Therefore, we need to pass both self and the token thru via defaults.
# note: we don't put this into the input buffer because it may have been
# @-unescaped already.
def give_it_back(self=self, token=token):
del self.get
return token
self.get = give_it_back
class Parser:
def parse_rcs_admin(self):
while 1:
# Read initial token at beginning of line
token = self.ts.get()
# We're done once we reach the description of the RCS tree
if token[0] in string.digits:
self.ts.unget(token)
return
if token == "head":
self.sink.set_head_revision(self.ts.get())
self.ts.match(';')
elif token == "branch":
self.sink.set_principal_branch(self.ts.get())
self.ts.match(';')
elif token == "symbols":
while 1:
tag = self.ts.get()
if tag == ';':
break
(tag_name, tag_rev) = string.split(tag, ':')
self.sink.define_tag(tag_name, tag_rev)
elif token == "comment":
self.sink.set_comment(self.ts.get())
self.ts.match(';')
# Ignore all these other fields - We don't care about them. Also chews
# up "newphrase".
elif token in ("locks", "strict", "expand", "access"):
while 1:
tag = self.ts.get()
if tag == ';':
break
else:
pass
# warn("Unexpected RCS token: $token\n")
raise RuntimeError, "Unexpected EOF";
def parse_rcs_tree(self):
while 1:
revision = self.ts.get()
# End of RCS tree description ?
if revision == 'desc':
self.ts.unget(revision)
return
# Parse date
self.ts.match('date')
date = self.ts.get()
self.ts.match(';')
# Convert date into timestamp
date_fields = string.split(date, '.') + ['0', '0', '0']
date_fields = map(string.atoi, date_fields)
if date_fields[0] < 100:
date_fields[0] = date_fields[0] + 1900
timestamp = time.mktime(tuple(date_fields))
# Parse author
self.ts.match('author')
author = self.ts.get()
self.ts.match(';')
# Parse state
self.ts.match('state')
state = ''
while 1:
token = self.ts.get()
if token == ';':
break
state = state + token + ' '
state = state[:-1] # toss the trailing space
# Parse branches
self.ts.match('branches')
branches = [ ]
while 1:
token = self.ts.get()
if token == ';':
break
branches.append(token)
# Parse revision of next delta in chain
self.ts.match('next')
next = self.ts.get()
if next == ';':
next = None
else:
self.ts.match(';')
# there are some files with extra tags in them. for example:
# owner 640;
# group 15;
# permissions 644;
# hardlinks @configure.in@;
# this is "newphrase" in RCSFILE(5). we just want to skip over these.
while 1:
token = self.ts.get()
if token == 'desc' or token[0] in string.digits:
self.ts.unget(token)
break
# consume everything up to the semicolon
while self.ts.get() != ';':
pass
self.sink.define_revision(revision, timestamp, author, state, branches,
next)
def parse_rcs_description(self):
self.ts.match('desc')
self.sink.set_description(self.ts.get())
def parse_rcs_deltatext(self):
while 1:
revision = self.ts.get()
if revision is None:
# EOF
break
self.ts.match('log')
log = self.ts.get()
### need to add code to chew up "newphrase"
self.ts.match('text')
text = self.ts.get()
self.sink.set_revision_info(revision, log, text)
def parse(self, file, sink):
self.ts = _TokenStream(file)
self.sink = sink
self.parse_rcs_admin()
self.parse_rcs_tree()
# many sinks want to know when the tree has been completed so they can
# do some work to prep for the arrival of the deltatext
self.sink.tree_completed()
self.parse_rcs_description()
self.parse_rcs_deltatext()
# easiest for us to tell the sink it is done, rather than worry about
# higher level software doing it.
self.sink.parse_completed()
self.ts = self.sink = None
class Sink:
def set_head_revision(self, revision):
pass
def set_principal_branch(self, branch_name):
pass
def define_tag(self, name, revision):
pass
def set_comment(self, comment):
pass
def set_description(self, description):
pass
def define_revision(self, revision, timestamp, author, state,
branches, next):
pass
def set_revision_info(self, revision, log, text):
pass
def tree_completed(self):
pass
def parse_completed(self):
pass
# --------------------------------------------------------------------------
#
# TESTING AND DEBUGGING TOOLS
#
class DebugSink:
def set_head_revision(self, revision):
print 'head:', revision
def set_principal_branch(self, branch_name):
print 'branch:', branch_name
def define_tag(self, name, revision):
print 'tag:', name, '=', revision
def set_comment(self, comment):
print 'comment:', comment
def set_description(self, description):
print 'description:', description
def define_revision(self, revision, timestamp, author, state,
branches, next):
print 'revision:', revision
print ' timestamp:', timestamp
print ' author:', author
print ' state:', state
print ' branches:', branches
print ' next:', next
def set_revision_info(self, revision, log, text):
print 'revision:', revision
print ' log:', log
print ' text:', text[:100], '...'
class DumpSink:
"""Dump all the parse information directly to stdout.
The output is relatively unformatted and untagged. It is intended as a
raw dump of the data in the RCS file. A copy can be saved, then changes
made to the parsing engine, then a comparison of the new output against
the old output.
"""
def __init__(self):
global sha
import sha
def set_head_revision(self, revision):
print revision
def set_principal_branch(self, branch_name):
print branch_name
def define_tag(self, name, revision):
print name, revision
def set_comment(self, comment):
print comment
def set_description(self, description):
print description
def define_revision(self, revision, timestamp, author, state,
branches, next):
print revision, timestamp, author, state, branches, next
def set_revision_info(self, revision, log, text):
print revision, sha.new(log).hexdigest(), sha.new(text).hexdigest()
def tree_completed(self):
print 'tree_completed'
def parse_completed(self):
print 'parse_completed'
def dump_file(fname):
Parser().parse(open(fname), DumpSink())
def time_file(fname):
import time
p = Parser().parse
f = open(fname)
s = Sink()
t = time.time()
p(f, s)
t = time.time() - t
print t
def _usage():
print 'This is normally a module for importing, but it has a couple'
print 'features for testing as an executable script.'
print 'USAGE: %s COMMAND filename,v' % sys.argv[0]
print ' where COMMAND is one of:'
print ' dump: filename is "dumped" to stdout'
print ' time: filename is parsed with the time written to stdout'
sys.exit(1)
if __name__ == '__main__':
import sys
if len(sys.argv) != 3:
usage()
if sys.argv[1] == 'dump':
dump_file(sys.argv[2])
elif sys.argv[1] == 'time':
time_file(sys.argv[2])
else:
usage()

339
lib/rlog.py Normal file
View File

@@ -0,0 +1,339 @@
# -*- Mode: python -*-
#
# Copyright (C) 2000-2001 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 ViewCVS
# distribution or at http://viewcvs.sourceforge.net/license-1.html.
#
# Contact information:
# Greg Stein, PO Box 760, Palo Alto, CA, 94302
# gstein@lyra.org, http://viewcvs.sourceforge.net/
#
# -----------------------------------------------------------------------
#
import os
import string
import re
import time
## RLogOutputParser uses the output of rlog to build a list of Commit
## objects describing all the checkins from a given RCS file; this
## parser is fairly optimized, and therefore can be delicate if the
## rlog output varies between versions of rlog; I don't know if it does;
## to make really fast, I should wrap the C rcslib
## there's definately not much error checking here; I'll assume things
## will go smoothly, and trap errors via exception handlers above this
## function
## exception for this class
error = 'rlog error'
class RLogData:
"Container object for all data parsed from a 'rlog' output."
def __init__(self, filename):
self.filename = filename
self.symbolic_name_hash = {}
self.rlog_entry_list = []
def LookupBranch(self, rlog_entry):
index = string.rfind(rlog_entry.revision, '.')
branch_revision = rlog_entry.revision[:index]
try:
branch = self.symbolic_name_hash[branch_revision]
except KeyError:
branch = ''
return branch
class RLogEntry:
## static constants for type of log entry; this will be changed
## to strings I guess -JMP
CHANGE = 0
ADD = 1
REMOVE = 2
## Here's the init function, which isn't needed since this class
## is fully initalized by RLogParser when creating a new log entry.
## Let's keep this initializer as a description of what is held in
## the class, but keep it commented out since it only makes things
## slow.
##
## def __init__(self):
## self.revision = ''
## self.author = ''
## self.branch = ''
## self.pluscount = ''
## self.minuscount = ''
## self.description = ''
## self.time = None
## self.type = RLogEntry.CHANGE
class RLog:
"Provides a alternative file-like interface for running 'rlog'."
def __init__(self, cfg, filename, revision, date):
self.filename = self.fix_filename(filename)
self.checkout_filename = self.create_checkout_filename(self.filename)
self.revision = revision
self.date = date
arg_list = []
if self.revision:
arg_list.append('-r%s' % (self.revision))
if self.date:
arg_list.append('-d%s' % (self.date))
temp = os.path.join(cfg.general.rcs_path, "rlog")
self.cmd = '%s %s "%s"' % (temp, string.join(arg_list), self.filename)
self.rlog = os.popen(self.cmd, 'r')
def fix_filename(self, filename):
## all RCS files have the ",v" ending
if filename[-2:] != ",v":
filename = "%s,v" % (filename)
if os.path.isfile(filename):
return filename
## check the Attic for the RCS file
path, basename = os.path.split(filename)
filename = os.path.join(path, "Attic", basename)
if os.path.isfile(filename):
return filename
raise error, "rlog file not found: %s" % (filename)
def create_checkout_filename(self, filename):
## cut off the ",v"
checkout_filename = filename[:-2]
## check if the file is in the Attic
path, basename = os.path.split(checkout_filename)
if path[-6:] != '/Attic':
return checkout_filename
## remove the "Attic" part of the path
checkout_filename = os.path.join(path[:-6], basename)
return checkout_filename
def readline(self):
try:
line = self.rlog.readline()
except AttributeError:
self.error()
if line:
return line
status = self.close()
if status:
self.error()
return None
def close(self):
status = self.rlog.close()
self.rlog = None
return status
def error(self):
raise error, "unexpected rlog exit: %s" % (self.cmd)
## constants used in the output parser
_rlog_commit_sep = '----------------------------\n'
_rlog_end = '=============================================================================\n'
## regular expression used in the output parser
_re_symbolic_name = re.compile("\s+([^:]+):\s+(.+)$")
_re_revision = re.compile("^revision\s+([0-9.]+).*")
_re_data_line = re.compile(
"^date:\s+(\d+)/(\d+)/(\d+)\s+(\d+):(\d+):(\d+);\s+"\
"author:\s+([^;]+);\s+"\
"state:\s+([^;]+);\s+"\
"lines:\s+\+(\d+)\s+\-(\d+)$")
_re_data_line_add = re.compile(
"^date:\s+(\d+)/(\d+)/(\d+)\s+(\d+):(\d+):(\d+);\s+"\
"author:\s+([^;]+);\s+"\
"state:\s+([^;]+);$")
class RLogOutputParser:
def __init__(self, rlog):
self.rlog = rlog
self.rlog_data = RLogData(rlog.checkout_filename)
## run the parser
self.parse_to_symbolic_names()
self.parse_symbolic_names()
self.parse_to_description()
self.parse_rlog_entries()
def parse_to_symbolic_names(self):
while 1:
line = self.rlog.readline()
if line[:15] == 'symbolic names:':
break
def parse_symbolic_names(self):
## parse all the tags int the branch_hash, it's used later to get
## the text names of non-head branches
while 1:
line = self.rlog.readline()
match = _re_symbolic_name.match(line)
if not match:
break
(tag, revision) = match.groups()
## check if the tag represents a branch, in RCS this means
## the second-to-last number is a zero
index = string.rfind(revision, '.')
if revision[index-2:index] == '.0':
revision = revision[:index-2] + revision[index:]
self.rlog_data.symbolic_name_hash[revision] = tag
def parse_to_description(self):
while 1:
line = self.rlog.readline()
if line[:12] == 'description:':
break
## eat all lines until we reach '-----' seperator
while 1:
line = self.rlog.readline()
if line == _rlog_commit_sep:
break
def parse_rlog_entries(self):
while 1:
rlog_entry = self.parse_one_rlog_entry()
if not rlog_entry:
break
self.rlog_data.rlog_entry_list.append(rlog_entry)
def parse_one_rlog_entry(self):
## revision line/first line
line = self.rlog.readline()
# Since FreeBSD's rlog outputs extra "---...---\n" before
# "===...===\n", _rlog_end may be occured here.
if not line or line == _rlog_end:
return None
## revision
match = _re_revision.match(line)
(revision,) = match.groups()
## data line
line = self.rlog.readline()
match = _re_data_line.match(line)
if not match:
match = _re_data_line_add.match(line)
if not match:
raise error, "bad rlog parser, no cookie!"
## retrieve the matched grops as a tuple in hopes
## this will be faster (ala profiler)
groups = match.groups()
year = string.atoi(groups[0])
month = string.atoi(groups[1])
day = string.atoi(groups[2])
hour = string.atoi(groups[3])
minute = string.atoi(groups[4])
second = string.atoi(groups[5])
author = groups[6]
state = groups[7]
## very strange; here's the deal: if this is a newly added file,
## then there is no plus/minus count count of lines; if there
## is, then this could be a "CHANGE" or "REMOVE", you can tell
## if the file has been removed by looking if state == 'dead'
try:
pluscount = groups[8]
minuscount = groups[9]
except IndexError:
pluscount = ''
minuscount = ''
cmit_type = RLogEntry.ADD
else:
if state == 'dead':
cmit_type = RLogEntry.REMOVE
else:
cmit_type = RLogEntry.CHANGE
## branch line: pretty much ignored if it's there
desc_line_list = []
line = self.rlog.readline()
if not line[:10] == 'branches: ':
desc_line_list.append(string.rstrip(line))
## suck up description
while 1:
line = self.rlog.readline()
## the last line printed out by rlog is '===='...
## or '------'... between entries
if line == _rlog_commit_sep or line == _rlog_end:
break
## append line to the descripton list
desc_line_list.append(string.rstrip(line))
## compute time using time routines in seconds from epoc GMT
## NOTE: mktime's arguments are in local time, and we have
## them in GMT from RCS; therefore, we have to manually
## subtract out the timezone correction
##
## XXX: Linux glib2.0.7 bug: it looks like mktime doesn't honor
## the '0' flag to force no timezone correction, so we look
## at the correction ourself and do the right thing after
## mktime mangles the date
gmt_time = \
time.mktime((year, month, day, hour, minute, second, 0, 0, -1))
if time.localtime(gmt_time)[8] == 1:
# dst time active?
# XXX: This is still wrong in those both nights,
# where the switch between DST and normal time occurs.
gmt_time = gmt_time - time.altzone
else:
gmt_time = gmt_time - time.timezone
## now create and return the RLogEntry
rlog_entry = RLogEntry()
rlog_entry.type = cmit_type
rlog_entry.revision = revision
rlog_entry.author = author
rlog_entry.description = string.join(desc_line_list, '\n')
rlog_entry.time = gmt_time
rlog_entry.pluscount = pluscount
rlog_entry.minuscount = minuscount
return rlog_entry
## entrypoints
def GetRLogData(cfg, path, revision = '', date = ''):
rlog = RLog(cfg, path, revision, date)
rlog_parser = RLogOutputParser(rlog)
return rlog_parser.rlog_data

2651
lib/viewcvs.py Normal file

File diff suppressed because it is too large Load Diff

514
standalone.py Executable file
View File

@@ -0,0 +1,514 @@
#!/usr/bin/env python
# $Id$
# vim:sw=4:ts=4:et:nowrap
# [Emacs: -*- python -*-]
#
# Copyright (C) 1999-2001 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 ViewCVS
# distribution or at http://viewcvs.sourceforge.net/license-1.html.
#
# Contact information:
# This file: Peter Funk, Oldenburger Str.86, 27777 Ganderkesee, Germany
# ViewCVS project: Greg Stein, PO Box 760, Palo Alto, CA, 94302
# gstein@lyra.org, http://viewcvs.sourceforge.net/
#
# Note: this module is designed to deploy instantly and run under any
# version of Python from 1.5 and up. That's why some 2.0 features
# (like string methods) are conspicuously avoided.
# XXX Security issues?
"""Run "standalone.py -p <port>" to start an HTTP server on a given port
on the local machine to generate ViewCVS web pages.
"""
__author__ = "Peter Funk <pf@artcom-gmbh.de>"
__date__ = "11 November 2001"
__version__ = "$Revision$"
__credits__ = """Guido van Rossum, for an excellent programming language.
Greg Stein, for writing ViewCVS in the first place.
Ka-Ping Yee, for the GUI code and the framework stolen from pydoc.py.
"""
# INSTALL-TIME CONFIGURATION
#
# This value will be set during the installation process. During
# development, it will remain None.
#
LIBRARY_DIR = None
import sys
import os
import string
import urllib
import socket
import select
import BaseHTTPServer
class Options:
port = 7467 # default TCP/IP port used for the server
start_gui = 0 # No GUI unless requested.
repository = None # use default repository specified in config
# --- web browser interface: ----------------------------------------------
def serve(port, callback=None):
"""start a HTTP server on the given port. call 'callback' when the
server is ready to serve"""
class ViewCVS_Handler(BaseHTTPServer.BaseHTTPRequestHandler):
def do_GET(self):
"""Serve a GET request."""
if not self.path or self.path == "/":
self.redirect()
elif self.is_viewcvs():
self.run_viewcvs()
elif self.path[:7] == "/icons/":
# XXX icon type should not be hardcoded to GIF:
self.send_response(200)
self.send_header("Content-type", "image/gif")
self.end_headers()
apache_icons.serve_icon(self.path, self.wfile)
else:
self.send_error(404)
def do_POST(self):
"""Serve a POST request."""
if self.is_viewcvs():
self.run_viewcvs()
else:
self.send_error(501, "Can only POST to viewcvs")
def is_viewcvs(self):
"""Check whether self.path matches the hardcoded ScriptAlias
/viewcvs"""
if self.path[:8] == "/viewcvs":
return 1
return 0
def redirect(self):
"""redirect the browser to the viewcvs URL"""
self.send_response(301, "moved (redirection follows)")
self.send_header("Content-type", "text/html")
self.send_header("Location", self.server.url + 'viewcvs/')
self.end_headers()
self.wfile.write("""<html>
<head>
<meta http-equiv="refresh" content="1; URL=%s">
</head>
<body>
<h1>Redirection to <a href="%s">ViewCVS</a></h1>
Wait a second. You will be automatically redirected to <b>ViewCVS</b>.
If this doesn't work, please click on the link above.
</body>
</html>
""" % tuple([self.server.url + "viewcvs/"]*2))
def run_viewcvs(self):
"""This is a quick and dirty cut'n'rape from Pythons
standard library module CGIHTTPServer."""
assert self.path[:8] == "/viewcvs"
viewcvs_url, rest = self.server.url[:-1]+"/viewcvs", self.path[8:]
i = string.rfind(rest, '?')
if i >= 0:
rest, query = rest[:i], rest[i+1:]
else:
query = ''
i = string.find(rest, '/')
if i >= 0:
script, rest = rest[:i], rest[i:]
else:
script, rest = rest, ''
scriptname = viewcvs_url + script
# sys.stderr.write("Debug: '"+scriptname+"' '"+rest+"' '"+query+"'\n")
env = os.environ
# Since we're going to modify the env in the parent, provide empty
# values to override previously set values
for k in ('QUERY_STRING', 'REMOTE_HOST', 'CONTENT_LENGTH',
'HTTP_USER_AGENT', 'HTTP_COOKIE'):
if env.has_key(k):
env[k] = ""
# XXX Much of the following could be prepared ahead of time!
env['SERVER_SOFTWARE'] = self.version_string()
env['SERVER_NAME'] = self.server.server_name
env['GATEWAY_INTERFACE'] = 'CGI/1.1'
env['SERVER_PROTOCOL'] = self.protocol_version
env['SERVER_PORT'] = str(self.server.server_port)
env['REQUEST_METHOD'] = self.command
uqrest = urllib.unquote(rest)
env['PATH_INFO'] = uqrest
env['SCRIPT_NAME'] = scriptname
if query:
env['QUERY_STRING'] = query
host = self.address_string()
if host != self.client_address[0]:
env['REMOTE_HOST'] = host
env['REMOTE_ADDR'] = self.client_address[0]
# AUTH_TYPE
# REMOTE_USER
# REMOTE_IDENT
if self.headers.typeheader is None:
env['CONTENT_TYPE'] = self.headers.type
else:
env['CONTENT_TYPE'] = self.headers.typeheader
length = self.headers.getheader('content-length')
if length:
env['CONTENT_LENGTH'] = length
accept = []
for line in self.headers.getallmatchingheaders('accept'):
if line[:1] in string.whitespace:
accept.append(string.strip(line))
else:
accept = accept + string.split(line[7:], ',')
env['HTTP_ACCEPT'] = string.joinfields(accept, ',')
ua = self.headers.getheader('user-agent')
if ua:
env['HTTP_USER_AGENT'] = ua
# XXX Other HTTP_* headers
decoded_query = string.replace(query, '+', ' ')
self.send_response(200)
# FIXME: I'm not sure about this: Sometimes it hurts, sometimes
# it is required. Please enlight me
if 1:
self.send_header("Content-type", "text/html")
self.end_headers()
# Preserve state, because we execute script in current process:
save_argv = sys.argv
save_stdin = sys.stdin
save_stdout = sys.stdout
save_stderr = sys.stderr
# For external tools like enscript we also need to redirect
# the real stdout file descriptor:
save_realstdout = os.dup(1)
try:
try:
sys.stdout = self.wfile
os.close(1)
assert os.dup(self.wfile.fileno()) == 1
sys.stdin = self.rfile
viewcvs.run_cgi()
finally:
sys.argv = save_argv
sys.stdin = save_stdin
sys.stdout.flush()
os.close(1)
assert os.dup(save_realstdout) == 1
sys.stdout = save_stdout
sys.stderr = save_stderr
except SystemExit, status:
self.log_error("ViewCVS exit status %s", str(status))
else:
self.log_error("ViewCVS exited ok")
class ViewCVS_Server(BaseHTTPServer.HTTPServer):
def __init__(self, port, callback):
host = (sys.platform == 'mac') and '127.0.0.1' or 'localhost'
self.address = ('', port)
self.url = 'http://%s:%d/' % (host, port)
self.callback = callback
BaseHTTPServer.HTTPServer.__init__(self, self.address,
self.handler)
def serve_until_quit(self):
self.quit = 0
while not self.quit:
rd, wr, ex = select.select([self.socket.fileno()], [], [], 1)
if rd:
self.handle_request()
def server_activate(self):
BaseHTTPServer.HTTPServer.server_activate(self)
if self.callback:
self.callback(self)
def server_bind(self):
# set SO_REUSEADDR (if available on this platform)
if hasattr(socket, 'SOL_SOCKET') \
and hasattr(socket, 'SO_REUSEADDR'):
self.socket.setsockopt(socket.SOL_SOCKET,
socket.SO_REUSEADDR, 1)
BaseHTTPServer.HTTPServer.server_bind(self)
ViewCVS_Server.handler = ViewCVS_Handler
try:
# XXX Move this code out of this function.
# Early loading of configuration here. Used to
# allow tinkering with some configuration settings:
viewcvs.handle_config()
if options.repository:
if viewcvs.cfg.general.cvs_roots.has_key("Development"):
viewcvs.cfg.general.cvs_roots["Development"] = options.repository
else:
sys.stderr.write("*** No default ViewCVS configuration. Edit viewcvs.conf\n")
raise KeyboardInterrupt # Hack!
elif viewcvs.cfg.general.cvs_roots.has_key("Development") and \
not os.path.isdir(viewcvs.cfg.general.cvs_roots["Development"]):
sys.stderr.write("*** No repository found. Please use the -r option.\n")
sys.stderr.write(" Use --help for more info.\n")
raise KeyboardInterrupt # Hack!
os.close(0) # To avoid problems with shell job control
ViewCVS_Server(port, callback).serve_until_quit()
except (KeyboardInterrupt, select.error):
pass
print 'server stopped'
# --- graphical interface: --------------------------------------------------
def gui(port):
"""Graphical interface (starts web server and pops up a control window)."""
class GUI:
def __init__(self, window, port):
self.window = window
self.server = None
self.scanner = None
import Tkinter
self.server_frm = Tkinter.Frame(window)
self.title_lbl = Tkinter.Label(self.server_frm,
text='Starting server...\n ')
self.open_btn = Tkinter.Button(self.server_frm,
text='open browser', command=self.open, state='disabled')
self.quit_btn = Tkinter.Button(self.server_frm,
text='quit serving', command=self.quit, state='disabled')
self.window.title('ViewCVS standalone')
self.window.protocol('WM_DELETE_WINDOW', self.quit)
self.title_lbl.pack(side='top', fill='x')
self.open_btn.pack(side='left', fill='x', expand=1)
self.quit_btn.pack(side='right', fill='x', expand=1)
# Early loading of configuration here. Used to
# allow tinkering with configuration settings through the gui:
viewcvs.handle_config()
if not LIBRARY_DIR:
viewcvs.cfg.options.cvsgraph_conf = "../cgi/cvsgraph.conf.dist"
self.options_frm = Tkinter.Frame(window)
# cvsgraph toggle:
self.cvsgraph_ivar = Tkinter.IntVar()
self.cvsgraph_ivar.set(viewcvs.cfg.options.use_cvsgraph)
self.cvsgraph_toggle = Tkinter.Checkbutton(self.options_frm,
text="enable cvsgraph (needs binary)", var=self.cvsgraph_ivar,
command=self.toggle_use_cvsgraph)
self.cvsgraph_toggle.pack(side='top', anchor='w')
# enscript toggle:
self.enscript_ivar = Tkinter.IntVar()
self.enscript_ivar.set(viewcvs.cfg.options.use_enscript)
self.enscript_toggle = Tkinter.Checkbutton(self.options_frm,
text="enable enscript (needs binary)", var=self.enscript_ivar,
command=self.toggle_use_enscript)
self.enscript_toggle.pack(side='top', anchor='w')
# show_subdir_lastmod toggle:
self.subdirmod_ivar = Tkinter.IntVar()
self.subdirmod_ivar.set(viewcvs.cfg.options.show_subdir_lastmod)
self.subdirmod_toggle = Tkinter.Checkbutton(self.options_frm,
text="show subdir last mod (dir view)", var=self.subdirmod_ivar,
command=self.toggle_subdirmod)
self.subdirmod_toggle.pack(side='top', anchor='w')
# use_re_search toggle:
self.useresearch_ivar = Tkinter.IntVar()
self.useresearch_ivar.set(viewcvs.cfg.options.use_re_search)
self.useresearch_toggle = Tkinter.Checkbutton(self.options_frm,
text="allow regular expr search", var=self.useresearch_ivar,
command=self.toggle_useresearch)
self.useresearch_toggle.pack(side='top', anchor='w')
# directory view template:
self.dirtemplate_lbl = Tkinter.Label(self.options_frm,
text='Chooose HTML Template for the Directory pages:')
self.dirtemplate_lbl.pack(side='top', anchor='w')
self.dirtemplate_svar = Tkinter.StringVar()
self.dirtemplate_svar.set(viewcvs.cfg.templates.directory)
self.dirtemplate_entry = Tkinter.Entry(self.options_frm,
width = 40, textvariable=self.dirtemplate_svar)
self.dirtemplate_entry.bind('<Return>', self.set_templates_directory)
self.dirtemplate_entry.pack(side='top', anchor='w')
self.templates_dir = Tkinter.Radiobutton(self.options_frm,
text="directory.ezt", value="templates/directory.ezt",
var=self.dirtemplate_svar, command=self.set_templates_directory)
self.templates_dir.pack(side='top', anchor='w')
self.templates_dir_alt = Tkinter.Radiobutton(self.options_frm,
text="dir_alternate.ezt", value="templates/dir_alternate.ezt",
var=self.dirtemplate_svar, command=self.set_templates_directory)
self.templates_dir_alt.pack(side='top', anchor='w')
# log view template:
self.logtemplate_lbl = Tkinter.Label(self.options_frm,
text='Chooose HTML Template for the Log pages:')
self.logtemplate_lbl.pack(side='top', anchor='w')
self.logtemplate_svar = Tkinter.StringVar()
self.logtemplate_svar.set(viewcvs.cfg.templates.log)
self.logtemplate_entry = Tkinter.Entry(self.options_frm,
width = 40, textvariable=self.logtemplate_svar)
self.logtemplate_entry.bind('<Return>', self.set_templates_log)
self.logtemplate_entry.pack(side='top', anchor='w')
self.templates_log = Tkinter.Radiobutton(self.options_frm,
text="log.ezt", value="templates/log.ezt",
var=self.logtemplate_svar, command=self.set_templates_log)
self.templates_log.pack(side='top', anchor='w')
self.templates_log_table = Tkinter.Radiobutton(self.options_frm,
text="log_table.ezt", value="templates/log_table.ezt",
var=self.logtemplate_svar, command=self.set_templates_log)
self.templates_log_table.pack(side='top', anchor='w')
# query view template:
self.querytemplate_lbl = Tkinter.Label(self.options_frm,
text='Template for the database query page:')
self.querytemplate_lbl.pack(side='top', anchor='w')
self.querytemplate_svar = Tkinter.StringVar()
self.querytemplate_svar.set(viewcvs.cfg.templates.query)
self.querytemplate_entry = Tkinter.Entry(self.options_frm,
width = 40, textvariable=self.querytemplate_svar)
self.querytemplate_entry.bind('<Return>', self.set_templates_query)
self.querytemplate_entry.pack(side='top', anchor='w')
self.templates_query = Tkinter.Radiobutton(self.options_frm,
text="query.ezt", value="templates/query.ezt",
var=self.querytemplate_svar, command=self.set_templates_query)
self.templates_query.pack(side='top', anchor='w')
# pack and set window manager hints:
self.server_frm.pack(side='top', fill='x')
self.options_frm.pack(side='top', fill='x')
self.window.update()
self.minwidth = self.window.winfo_width()
self.minheight = self.window.winfo_height()
self.expanded = 0
self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight))
self.window.wm_minsize(self.minwidth, self.minheight)
import threading
threading.Thread(target=serve, args=(port, self.ready)).start()
def toggle_use_cvsgraph(self, event=None):
viewcvs.cfg.options.use_cvsgraph = self.cvsgraph_ivar.get()
def toggle_use_enscript(self, event=None):
viewcvs.cfg.options.use_enscript = self.enscript_ivar.get()
def toggle_subdirmod(self, event=None):
viewcvs.cfg.options.show_subdir_lastmod = self.subdirmod_ivar.get()
def toggle_useresearch(self, event=None):
viewcvs.cfg.options.use_re_search = self.useresearch_ivar.get()
def set_templates_log(self, event=None):
viewcvs.cfg.templates.log = self.logtemplate_svar.get()
def set_templates_directory(self, event=None):
viewcvs.cfg.templates.directory = self.dirtemplate_svar.get()
def set_templates_query(self, event=None):
viewcvs.cfg.templates.query = self.querytemplate_svar.get()
def ready(self, server):
"""used as callback parameter to the serve() function"""
self.server = server
self.title_lbl.config(
text='ViewCVS standalone server at\n' + server.url)
self.open_btn.config(state='normal')
self.quit_btn.config(state='normal')
def open(self, event=None, url=None):
"""opens a browser window on the local machine"""
url = url or self.server.url
try:
import webbrowser
webbrowser.open(url)
except ImportError: # pre-webbrowser.py compatibility
if sys.platform == 'win32':
os.system('start "%s"' % url)
elif sys.platform == 'mac':
try:
import ic
ic.launchurl(url)
except ImportError: pass
else:
rc = os.system('netscape -remote "openURL(%s)" &' % url)
if rc: os.system('netscape "%s" &' % url)
def quit(self, event=None):
if self.server:
self.server.quit = 1
self.window.quit()
import Tkinter
try:
gui = GUI(Tkinter.Tk(), port)
Tkinter.mainloop()
except KeyboardInterrupt:
pass
# --- command-line interface: ----------------------------------------------
def cli(argv):
"""Command-line interface (looks at argv to decide what to do)."""
import getopt
class BadUsage(Exception): pass
try:
opts, args = getopt.getopt(argv[1:], 'gp:r:',
['gui', 'port=', 'repository='])
for opt, val in opts:
if opt in ('-g', '--gui'):
options.start_gui = 1
elif opt in ('-r', '--repository'):
options.repository = val
elif opt in ('-p', '--port'):
try:
options.port = int(val)
except ValueError:
raise BadUsage
if options.start_gui:
gui(options.port)
return
elif options.port:
def ready(server):
print 'server ready at %s' % server.url
serve(options.port, ready)
return
raise BadUsage
except (getopt.error, BadUsage):
cmd = sys.argv[0]
port = options.port
print """ViewCVS standalone - a simple standalone HTTP-Server
Usage: %(cmd)s [ <options> ]
Available Options:
-p <port> or --port=<port>
Start an HTTP server on the given port on the local machine.
Default port is %(port)d.
-r <path> or --repository=<path>
Specify another path for the default CVS repository "Development".
If you don't have your repository at /home/cvsroot you will need to
use this option or you have to install first and edit viewcvs.conf.
-g or --gui
Pop up a graphical interface for serving and testing ViewCVS.
""" % locals()
if __name__ == '__main__':
if LIBRARY_DIR:
sys.path.insert(0, LIBRARY_DIR)
else:
sys.path[:0] = ['lib']
os.chdir('lib')
import viewcvs
import apache_icons
options = Options()
cli(sys.argv)

3
templates/annotate.ezt Normal file
View File

@@ -0,0 +1,3 @@
[include "header.ezt" "annotate"]
<hr noshade>

88
templates/diff.ezt Normal file
View File

@@ -0,0 +1,88 @@
[include "header.ezt" "diff"]
<h3 align=center>Diff for /[where] between version [rev1] and [rev2]</h3>
<table border=0 cellspacing=0 cellpadding=0 width="100%">
<tr bgcolor=white>
<th width="50%" valign=top>
version [rev1][date1]
[if-any tag1]<br>Tag: [tag1][end]
</th>
<th width="50%" valign=top>
version [rev2][date2]
[if-any tag2]<br>Tag: [tag2][end]
</th>
</tr>
[for changes]
[is changes.type "header"]
<tr bgcolor="#99cccc"><td width="50%">
<table width="100%" border=1 cellpadding=5><tr>
<td><b>Line [changes.line1]</b>&nbsp;<font
size="-1">[changes.extra]</font></td>
</tr></table></td><td width="50%">
<table width="100%" border=1 cellpadding=5><tr>
<td><b>Line [changes.line2]</b>&nbsp;<font
size="-1">[changes.extra]</font></td>
</tr></table>
</td></tr>
[else]
[is changes.type "add"]
<tr>
<td bgcolor="#cccccc">&nbsp;</td>
<td bgcolor="#aaffaa">[changes.right]</td>
</tr>
[else]
[is changes.type "remove"]
<tr>
<td bgcolor="#ffaaaa">[changes.left]</td>
<td bgcolor="#cccccc">&nbsp;</td>
</tr>
[else]
[is changes.type "change"]
<tr>
[if-any changes.have_left]<td bgcolor="#ffff77">[changes.left]</td>
[else]<td bgcolor="#eeee77">&nbsp;</td>[end]
[if-any changes.have_right]<td bgcolor="#ffff77">[changes.right]</td>
[else]<td bgcolor="#eeee77">&nbsp;</td>[end]
</tr>
[else]
[is changes.type "no-changes"]
<tr><td colspan=2>&nbsp;</td></tr>
<tr bgcolor="#cccccc"><td colspan=2 align=center>
<br><b>- No changes -</b><br>&nbsp;
</td></tr>
[else][# a line of context]
<tr><td>[changes.left]</td><td>[changes.right]</td></tr>
[end][end][end][end][end]
[end]
</table><br><hr noshade width="100%">
<table border=0 cellpadding=10><tr><td>
<table border=1><tr><td>Legend:<br>
<table border=0 cellspacing=0 cellpadding=1>
<tr>
<td align=center bgcolor="#ffaaaa">Removed from v.[rev1]</td>
<td bgcolor="#cccccc">&nbsp;</td>
</tr>
<tr bgcolor="#ffff77"><td align=center colspan=2>changed lines</td></tr>
<tr>
<td bgcolor="#cccccc">&nbsp;</td>
<td align=center bgcolor="#aaffaa">Added in v.[rev2]</td>
</tr>
</table></td></tr></table></td>
<td><form method="GET" action="[request.url]">
[hidden_values]
<select name="diff_format" onchange="submit()">
<option value="h" [is diff_format "h"]selected[end]>Colored Diff</option>
<option value="l" [is diff_format "l"]selected[end]>Long Colored Diff</option>
<option value="u" [is diff_format "u"]selected[end]>Unidiff</option>
<option value="c" [is diff_format "c"]selected[end]>Context Diff</option>
<option value="s" [is diff_format "s"]selected[end]>Side by Side</option>
</select>
<input type=submit value="Show">
</form></td></tr>
</table>
[include "footer.ezt"]

307
templates/dir_alternate.ezt Normal file
View File

@@ -0,0 +1,307 @@
<!doctype html public "-//W3C//DTD HTML 4.0 Transitional//EN"
"http://www.w3.org/TR/REC-html40/loose.dtd">
<html><head>
<!-- ViewCVS -- http://viewcvs.sourceforge.net/
by Greg Stein -- mailto:gstein@lyra.org
-->
<title>[if-any where][where][else][cfg.general.main_title][end]</title>
</head>
<body text="#000000" bgcolor="#ffffff">
<table width="100%" border=0 cellspacing=0 cellpadding=0>
<tr>
<td rowspan=2><h1>[if-any where][where][else][cfg.general.main_title][end]</h1></td>
<td align=right><img src="/icons/apache_pb.gif" alt="(logo)" border=0
width=259 height=32></td>
</tr>
<tr>
<td align=right><h3><b><a target="_blank"
href="[request.script_name]/*docroot*/help_[if-any where]dir[else]root[end]view.html">ViewCVS and CVS Help</a></b></h3></td>
</tr>
</table>
[if-any where][else]
<!-- you may insert repository access instructions here -->
[if-any roots]
<h3>Project Root</h3>
<form method=GET action="./">
<select name=cvsroot onchange="submit()">
[for roots]
[is roots current_root]
<option selected>[roots]</option>
[else]
<option>[roots]</option>
[end]
[end]
</select>
<input type=submit value="Go">
</form>
[end]
[end]
<p><a name="dirlist"></a></p>
[if-any where]
<table>
<tr><td>Current directory:</td><td><b>[nav_path]</b></td></tr>
[if-any view_tag]
<tr><td>Current tag:</td><td><b>[view_tag]</b></td></tr>
[end]
[if-any search_re]
<tr><td>Current search:</td><td><b>[search_re]</b></td></tr>
[end]
[is num_files "0"]
[else]
<tr><td>Files shown:</td><td><b>[files_shown]</b></td></tr>
[end]
</table>
[end]
<hr noshade>
[# if you want a colored border around the table of directory
information, then add this additional table tag:
<table border=0 cellpadding=0 width="100%"><tr>
<td bgcolor="#999999">
]
<table width="100%" border=0 cellspacing=1 cellpadding=2>
<tr>
[if-any have_logs]
[is sortby "rev"]
<th align=left bgcolor="#88ff88">Rev.</th>
[else]
<th align=left bgcolor="#cccccc"
><a href="./[sortby_rev_href]"#dirlist>Rev.</a></th>
[end]
[is sortby "file"]
<th align=left bgcolor="#88ff88"
[is cfg.options.use_cvsgraph "1"]colspan=2[end]
>File</th>
[else]
<th align=left bgcolor="#cccccc"
[is cfg.options.use_cvsgraph "1"]colspan=2[end]
><a href="./[sortby_file_href]#dirlist">File</a></th>
[end]
[is sortby "date"]
<th align=left bgcolor="#88ff88">Age</th>
[else]
<th align=left bgcolor="#cccccc"
><a href="./[sortby_date_href]#dirlist">Age</a></th>
[end]
[is sortby "author"]
<th align=left bgcolor="#88ff88">Author</th>
[else]
<th align=left bgcolor="#cccccc"
><a href="./[sortby_author_href]#dirlist">Author</a></th>
[end]
[is sortby "log"]
<th align=left bgcolor="#88ff88">Last log entry</th>
[else]
<th align=left bgcolor="#cccccc"
><a href="./[sortby_log_href]#dirlist">Last log entry</a></th>
[end]
[else]
<th align=left bgcolor="#cccccc">File</th>
[end]
</tr>
[for rows]
<tr bgcolor="[if-index rows even]#ffffff[else]#ccccee[end]">
[is rows.type "unreadable"]
[if-any have_logs]
<td>&nbsp;</td> [# revision ]
[end]
<td><a name="[rows.anchor]">[rows.name]</a>
[if-any have_logs]
</td>
<td colspan=[is cfg.options.use_cvsgraph "1"]4[else]3[end]>
<i>this entry is unreadable</i>
</td>
[else]
- <i>this entry is unreadable</i>
[end]
</td>
[else]
[is rows.type "dir"]
[if-any have_logs]
<td>&nbsp;</td> [# revision ]
[end]
<td [if-any have_logs][is cfg.options.use_cvsgraph "1"]colspan=2[end][end]>
<a name="[rows.anchor]" href="[rows.href]">
<img src="/icons/small/dir.gif" alt="(dir)" border=0 width=16 height=16>
[rows.name]
</a>
[is rows.name "Attic/"]
&nbsp; <a href="./[show_attic_href]#dirlist">[[]show contents]</a>
[end]
</td>
[is rows.cvs "error"]
[# for an error to occur, we must have some logs. always use colspan]
<td colspan=3>
<i>Last modification unavailable - could not read CVS information</i>
</td>
[else]
[if-any have_logs]
[is rows.cvs "none"]
<td>&nbsp;</td> [# age ]
<td>&nbsp;</td> [# author ]
<td>&nbsp;</td> [# log ]
[else]
<td>&nbsp;[rows.time]</td>
[if-any rows.author]
<td>&nbsp;[rows.author]</td>
[end]
[if-any rows.show_log]
<td>&nbsp;[rows.log_file]/[rows.log_rev]<br>
&nbsp;<font size="-1">[rows.log]</font></td>
[end]
[end]
[end]
[end]
[else]
[is rows.cvs "error"]
<td>&nbsp;</td>
<td [is cfg.options.use_cvsgraph "1"]colspan=2[end]>
<a name="[rows.anchor]">[rows.name]</a>
</td>
<td colspan=3><i>CVS information is unreadable</i></td>
[else]
<td>&nbsp;<a href="[rows.href]"><b>[rows.rev]</b></a></td>
<td width="1%"><a name="[rows.anchor]" href="[rows.rev_href]&content-type=text/vnd.viewcvs-markup">
[# to display the revision in a separate window, you could use:
<a name="{rows.anchor}" href="{rows.rev_href}" target="cvs_checkout"
onClick="window.open('{rows.rev_href}', 'cvs_checkout',
'resizeable=1,scrollbars=1')"><b>{rows.rev}</b></a>
(substitute brackets for the braces)
]
<img src="/icons/small/text.gif" alt="(file)" border=0
width=16 height=16>
[rows.name]
</a>
[is rows.state "dead"]
[# don't let this phrase/link be wrapped ]
[if-any view_tag](not&nbsp;exist)[else](in&nbsp;the&nbsp;Attic)[end]&nbsp;<a href="./[hide_attic_href]#dirlist">[[]hide]</a>
[end]
</td>
[if-any rows.graph_href]
<td width="1%"><a href="[rows.graph_href]"><img
src="[request.script_name]/*docroot*/images/cvsgraph_16x16.png"
alt="(graph)" width=16 height=16 border=0>
</a></td>
[end]
<td>&nbsp;[rows.time]</td>
[if-any rows.author]
<td>&nbsp;[rows.author]</td>
[end]
[if-any rows.show_log]
<td>&nbsp;[rows.log]</td>
[end]
[end]
[end]
[end]
</tr>
[end]
</table>
[# if you are using a table border (see above), then add:
</td></tr></table>
]
[if-any no_match]
<p><b>NOTE:</b> There are [num_files] files, but none match the
current selection criteria.
[end]
[if-any unreadable]
<hr size=1 noshade>
<b>NOTE:</b> One or more files were unreadable. The files in the CVS
repository should be readable by the web server process. Please
report this condition to the administrator of this CVS repository.
[end]
[if-any selection_form]
<hr size=1 noshade>
[# this table holds the selectors on the left, and reset on the right ]
<table><tr><td>
<form method=GET action="./">
[for params]
<input type=hidden name="[params.name]" value="[params.value]">
[end]
<table>
[if-any has_tags]
<tr>
<td>Show files using tag:</td>
<td>
<select name=only_with_tag onchange="submit()">
[if-any branch_tags]
<option value="">- Branches -</option>
[for branch_tags]
[is branch_tags view_tag]
<option selected>[branch_tags]</option>
[else]
<option>[branch_tags]</option>
[end]
[end]
[end]
<option value="">- Non-branch tags -</option>
[for plain_tags]
[is plain_tags view_tag]
<option selected>[plain_tags]</option>
[else]
<option>[plain_tags]</option>
[end]
[end]
</select></td>
</tr>
[end]
[is cfg.options.use_re_search "1"]
<tr>
<td>Show files containing the regular expression:</td>
<td><input type="text" name="search" value="[search_re]">
</tr>
[end]
<tr><td>&nbsp;</td><td><input type="submit" value="Show"></td></tr>
</table>
</form>
</td>
[if-any view_tag]
<td valign=bottom><form method=GET action="./">
[for params]
<input type=hidden name="[params.name]" value="[params.value]">
[end]
<input type="submit" value="Show all files">
</form></td>
[else][if-any search_re]
<td valign=bottom><form method=GET action="./">
[for params]
<input type=hidden name="[params.name]" value="[params.value]">
[end]
<input type="submit" value="Show all files">
</form></td>
[end][end]
</tr></table>
[end]
[# if you want to disable tarball generation remove the following: ]
[if-any tarball_href]
<a href="[tarball_href]">Download tarball</a>
[end]
[include "footer.ezt"]

305
templates/directory.ezt Normal file
View File

@@ -0,0 +1,305 @@
<!doctype html public "-//W3C//DTD HTML 4.0 Transitional//EN"
"http://www.w3.org/TR/REC-html40/loose.dtd">
<html><head>
<!-- ViewCVS -- http://viewcvs.sourceforge.net/
by Greg Stein -- mailto:gstein@lyra.org
-->
<title>[if-any where][where][else][cfg.general.main_title][end]</title>
</head>
<body text="#000000" bgcolor="#ffffff">
<table width="100%" border=0 cellspacing=0 cellpadding=0>
<tr>
<td rowspan=2><h1>[if-any where][where][else][cfg.general.main_title][end]</h1></td>
<td align=right><img src="/icons/apache_pb.gif" alt="(logo)" border=0
width=259 height=32></td>
</tr>
<tr>
<td align=right><h3><b><a target="_blank"
href="[request.script_name]/*docroot*/help_[if-any where]dir[else]root[end]view.html">ViewCVS and CVS Help</a></b></h3></td>
</tr>
</table>
[if-any where][else]
<!-- you may insert repository access instructions here -->
[if-any roots]
<h3>Project Root</h3>
<form method=GET action="./">
<select name=cvsroot onchange="submit()">
[for roots]
[is roots current_root]
<option selected>[roots]</option>
[else]
<option>[roots]</option>
[end]
[end]
</select>
<input type=submit value="Go">
</form>
[end]
[end]
<p><a name="dirlist"></a></p>
[if-any where]
<table>
<tr><td>Current directory:</td><td><b>[nav_path]</b></td></tr>
[if-any view_tag]
<tr><td>Current tag:</td><td><b>[view_tag]</b></td></tr>
[end]
[if-any search_re]
<tr><td>Current search:</td><td><b>[search_re]</b></td></tr>
[end]
[is num_files "0"]
[else]
<tr><td>Files shown:</td><td><b>[files_shown]</b></td></tr>
[end]
</table>
[end]
<hr noshade>
[# if you want a colored border around the table of directory
information, then add this additional table tag:
<table border=0 cellpadding=0 width="100%"><tr>
<td bgcolor="#999999">
]
<table width="100%" border=0 cellspacing=1 cellpadding=2>
<tr>
[if-any have_logs]
[is sortby "file"]
<th align=left bgcolor="#88ff88"
[is cfg.options.use_cvsgraph "1"]colspan=2[end]
>File</th>
[else]
<th align=left bgcolor="#cccccc"
[is cfg.options.use_cvsgraph "1"]colspan=2[end]
><a href="./[sortby_file_href]#dirlist">File</a></th>
[end]
[is sortby "rev"]
<th align=left bgcolor="#88ff88">Rev.</th>
[else]
<th align=left bgcolor="#cccccc"
><a href="./[sortby_rev_href]"#dirlist>Rev.</a></th>
[end]
[is sortby "date"]
<th align=left bgcolor="#88ff88">Age</th>
[else]
<th align=left bgcolor="#cccccc"
><a href="./[sortby_date_href]#dirlist">Age</a></th>
[end]
[is sortby "author"]
<th align=left bgcolor="#88ff88">Author</th>
[else]
<th align=left bgcolor="#cccccc"
><a href="./[sortby_author_href]#dirlist">Author</a></th>
[end]
[is sortby "log"]
<th align=left bgcolor="#88ff88">Last log entry</th>
[else]
<th align=left bgcolor="#cccccc"
><a href="./[sortby_log_href]#dirlist">Last log entry</a></th>
[end]
[else]
<th align=left bgcolor="#cccccc">File</th>
[end]
</tr>
[for rows]
<tr bgcolor="[if-index rows even]#ffffff[else]#ccccee[end]">
[is rows.type "unreadable"]
<td><a name="[rows.anchor]">[rows.name]</a>
[if-any have_logs]
</td>
<td colspan=[is cfg.options.use_cvsgraph "1"]5[else]4[end]>
<i>this entry is unreadable</i>
</td>
[else]
- <i>this entry is unreadable</i>
[end]
</td>
[else]
[is rows.type "dir"]
<td [if-any have_logs][is cfg.options.use_cvsgraph "1"]colspan=2[end][end]>
<a name="[rows.anchor]" href="[rows.href]">
<img src="/icons/small/dir.gif" alt="(dir)" border=0 width=16 height=16>
[rows.name]
</a>
[is rows.name "Attic/"]
&nbsp; <a href="./[show_attic_href]#dirlist">[[]show contents]</a>
[end]
</td>
[is rows.cvs "error"]
[# for an error to occur, we must have some logs. always use colspan]
<td colspan=4>
<i>Last modification unavailable - could not read CVS information</i>
</td>
[else]
[if-any have_logs]
<td>&nbsp;</td> [# revision ]
[is rows.cvs "none"]
<td>&nbsp;</td> [# age ]
<td>&nbsp;</td> [# author ]
<td>&nbsp;</td> [# log ]
[else]
<td>&nbsp;[rows.time]</td>
[if-any rows.author]
<td>&nbsp;[rows.author]</td>
[end]
[if-any rows.show_log]
<td>&nbsp;[rows.log_file]/[rows.log_rev]<br>
&nbsp;<font size="-1">[rows.log]</font></td>
[end]
[end]
[end]
[end]
[else]
[is rows.cvs "error"]
<td [is cfg.options.use_cvsgraph "1"]colspan=2[end]>
<a name="[rows.anchor]">[rows.name]</a>
</td>
<td colspan=4><i>CVS information is unreadable</i></td>
[else]
<td><a name="[rows.anchor]" href="[rows.href]">
<img src="/icons/small/text.gif" alt="(file)" border=0
width=16 height=16>
[rows.name]
</a>
[is rows.state "dead"]
[# don't let this phrase/link be wrapped ]
[if-any view_tag](not&nbsp;exist)[else](in&nbsp;the&nbsp;Attic)[end]&nbsp;<a href="./[hide_attic_href]#dirlist">[[]hide]</a>
[end]
</td>
[if-any rows.graph_href]
<td width="1%"><a href="[rows.graph_href]"><img
src="[request.script_name]/*docroot*/images/cvsgraph_16x16.png"
alt="(graph)" width=16 height=16 border=0>
</a></td>
[end]
<td>&nbsp;
<a href="[rows.rev_href]&content-type=text/vnd.viewcvs-markup">
<b>[rows.rev]</b>
</a>
[# to display the revision in a separate window, you could use:
<a href="{rows.rev_href}" target="cvs_checkout"
onClick="window.open('{rows.rev_href}', 'cvs_checkout',
'resizeable=1,scrollbars=1')"><b>{rows.rev}</b></a>
(substitute brackets for the braces)
]
</td>
<td>&nbsp;[rows.time]</td>
[if-any rows.author]
<td>&nbsp;[rows.author]</td>
[end]
[if-any rows.show_log]
<td>&nbsp;[rows.log]</td>
[end]
[end]
[end]
[end]
</tr>
[end]
</table>
[# if you are using a table border (see above), then add:
</td></tr></table>
]
[if-any no_match]
<p><b>NOTE:</b> There are [num_files] files, but none match the
current selection criteria.
[end]
[if-any unreadable]
<hr size=1 noshade>
<b>NOTE:</b> One or more files were unreadable. The files in the CVS
repository should be readable by the web server process. Please
report this condition to the administrator of this CVS repository.
[end]
[if-any selection_form]
<hr size=1 noshade>
[# this table holds the selectors on the left, and reset on the right ]
<table><tr><td>
<form method=GET action="./">
[for params]
<input type=hidden name="[params.name]" value="[params.value]">
[end]
<table>
[if-any has_tags]
<tr>
<td>Show files using tag:</td>
<td>
<select name=only_with_tag onchange="submit()">
[if-any branch_tags]
<option value="">- Branches -</option>
[for branch_tags]
[is branch_tags view_tag]
<option selected>[branch_tags]</option>
[else]
<option>[branch_tags]</option>
[end]
[end]
[end]
<option value="">- Non-branch tags -</option>
[for plain_tags]
[is plain_tags view_tag]
<option selected>[plain_tags]</option>
[else]
<option>[plain_tags]</option>
[end]
[end]
</select></td>
</tr>
[end]
[is cfg.options.use_re_search "1"]
<tr>
<td>Show files containing the regular expression:</td>
<td><input type="text" name="search" value="[search_re]">
</tr>
[end]
<tr><td>&nbsp;</td><td><input type="submit" value="Show"></td></tr>
</table>
</form>
</td>
[if-any view_tag]
<td valign=bottom><form method=GET action="./">
[for params]
<input type=hidden name="[params.name]" value="[params.value]">
[end]
<input type="submit" value="Show all files">
</form></td>
[else][if-any search_re]
<td valign=bottom><form method=GET action="./">
[for params]
<input type=hidden name="[params.name]" value="[params.value]">
[end]
<input type="submit" value="Show all files">
</form></td>
[end][end]
</tr></table>
[end]
[# if you want to disable tarball generation remove the following: ]
[if-any tarball_href]
<a href="[tarball_href]">Download tarball</a>
[end]
[include "footer.ezt"]

9
templates/footer.ezt Normal file
View File

@@ -0,0 +1,9 @@
[# standard footer used by all ViewCVS pages ]
<hr noshade>
<table width="100%" border=0 cellpadding=0 cellspacing=0><tr>
<td align=left><address>[cfg.general.address]</address></td>
<td align=right>
Powered by<br><a href="http://viewcvs.sourceforge.net/">ViewCVS [vsn]</a>
</td></tr></table>
</body></html>

12
templates/graph.ezt Normal file
View File

@@ -0,0 +1,12 @@
[include "header.ezt" "graph"]
<center>
<h1>Revision graph of [request.where]</h1>
[imagemap]
<img border="0" usemap="#MyMapName"
src="[request.url]?graph=[rev]&makeimage=1[request.amp_query]"
alt="Revisions of [request.where]">
</center>
[include "footer.ezt"]

25
templates/header.ezt Normal file
View File

@@ -0,0 +1,25 @@
<html><head>
<!-- ViewCVS -- http://viewcvs.sourceforge.net/
by Greg Stein -- mailto:gstein@lyra.org
-->
[### NOTE: the "diff" is the TITLE param to navigate_header() ]
<title>[path]/[filename] - [arg0] - [rev]</title>
</head>
<body bgcolor="#eeeeee">
<table width="100%" border=0 cellspacing=0 cellpadding=1 bgcolor="#9999ee">
<tr valign=bottom>
<td>
<a href="[file_url][qquery]#rev[rev]">
<img src="/icons/small/back.gif" alt="(file)" border=0 width=16 height=16>
</a>
<b>Return to
<a href="[file_url][qquery]#rev[rev]">[filename]</a>
CVS log</b>
<img src="/icons/small/text.gif" alt="(file)" border=0 width=16 height=16>
</td>
<td align=right>
<img src="/icons/small/dir.gif" alt="(dir)" border=0 width=16 height=16>
<b>Up to [nav_path]</b>
</td>
</tr>
</table>

246
templates/log.ezt Normal file
View File

@@ -0,0 +1,246 @@
<!doctype html public "-//W3C//DTD HTML 4.0 Transitional//EN"
"http://www.w3.org/TR/REC-html40/loose.dtd">
<html><head>
<!-- ViewCVS -- http://viewcvs.sourceforge.net/
by Greg Stein -- mailto:gstein@lyra.org
-->
<title>CVS log for [where]</title>
</head>
<body text="#000000" bgcolor="#ffffff">
<table width="100%" border=0 cellspacing=0 cellpadding=0>
<tr>
<td rowspan=2><h1>CVS log for [where]</h1></td>
<td align=right><img src="/icons/apache_pb.gif" alt="(logo)" border=0
width=259 height=32></td>
</tr>
<tr>
<td align=right><h3><b><a target="_blank"
href="[request.script_name]/*docroot*/help_log.html">Help</a></b></h3></td>
</tr>
</table>
<a href="[back_url]"><img src="/icons/small/back.gif" alt="(back)" border=0
width=16 height=16></a>
<b>Up to [nav_path]</b><p>
<a href="#diff">Request diff between arbitrary revisions</a>
[if-any graph_href]
/ <a href="[graph_href]">Display revisions graphically</a>
[end]
<hr noshade>
[if-any branch]
Default branch: [branch]
<br>
Bookmark a link to:
[if-any viewable]
<a href="[head_href]"><b>HEAD</b></a>
/
(<a href="[head_abs_href]" target="cvs_checkout"
onClick="window.open('about:blank', 'cvs_checkout',
'resizeable=1,scrollbars=1[is mime_type "text/html"],status,toolbar[end]')"
><b>download</b></a>)
[else]
<a href="[head_href]" target="cvs_checkout"
onClick="window.open('about:blank', 'cvs_checkout',
'resizeable=1,scrollbars=1[is mime_type "text/html"],status,toolbar[end]')"
><b>HEAD</b></a>
[end]
[else]
No default branch
[end]
<br>
[if-any view_tag]
Current tag: [view_tag] <br>
[end]
[for entries]
<hr size=1 noshade>
[is entries.state "dead"]
Revision <b>[entries.rev]</b>
[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
[if-any viewable]
<a href="[entries.href]" target="cvs_checkout"
onClick="window.open('about:blank', 'cvs_checkout',
'resizeable=1,scrollbars=1[is mime_type "text/html"],status,toolbar[end]')"
><b>[entries.rev]</b></a>
[else]
<a href="[entries.view_href]"><b>[entries.rev]</b></a>
/
(<a href="[entries.href]" target="cvs_checkout"
onClick="window.open('about:blank', 'cvs_checkout',
'resizeable=1,scrollbars=1[is mime_type "text/html"],status,toolbar[end]')"
><b>download</b></a>)
[end]
[is mime_type "text/plain"]
[else]
/
(<a href="[entries.text_href]" target="cvs_checkout"
onClick="window.open('about:blank', 'cvs_checkout',
'resizeable=1,scrollbars=1')"><b>as text</b></a>)
[end]
[if-any viewable]
/
<a href="[entries.view_href]"><b>(view)</b></a>
[end]
[# if you don't want to allow annotation, then remove this line]
- <a href="[href]?annotate=[entries.rev][query]">annotate</a>
[# if you don't want to allow select for diffs then remove this section]
[is entries.rev rev_selected]
- <b>[[]selected]</b>
[else]
- <a href="[href]?r1=[entries.rev][query]">[[]select for diffs]</a>
[end]
[end]
[if-any entries.vendor_branch]
<i>(vendor branch)</i>
[end]
, <i>[entries.utc_date] UTC</i> ([entries.ago] ago) by <i>[entries.author]</i>
[if-any entries.branches]
<br>Branch:
[for entries.branches]
<a href="[entries.branches.href]"><b>[entries.branches.name]</b></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]"><b>[entries.tags.name]</b></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]"><b>[entries.branch_points.name]</b></a>[if-index entries.branch_points last][else],[end]
[end]
[end]
[if-any entries.prev]
[if-any entries.changed]
<br>Changes since <b>[entries.prev]: [entries.changed] lines</b>
[end]
[end]
[is entries.state "dead"]
<br><b><i>FILE REMOVED</i></b>
[else]
[if-any entries.prev]
<br>Diff to <a href="[href].diff?r1=[entries.prev]&r2=[entries.rev][query]">previous [entries.prev]</a>
[if-any human_readable]
[else]
(<a href="[href].diff?r1=[entries.prev]&r2=[entries.rev]&diff_format=h[query]">colored</a>)
[end]
[end]
[if-any entries.branch_point]
to <a href="[href].diff?r1=[entries.branch_point]&r2=[entries.rev][query]">branch point [entries.branch_point]</a>
[if-any human_readable]
[else]
(<a href="[href].diff?r1=[entries.branch_point]&r2=[entries.rev]&diff_format=h[query]">colored</a>)
[end]
[end]
[if-any entries.next_main]
to <a href="[href].diff?r1=[entries.next_main]&r2=[entries.rev][query]">next main [entries.next_main]</a>
[if-any human_readable]
[else]
(<a href="[href].diff?r1=[entries.next_main]&r2=[entries.rev]&diff_format=h[query]">colored</a>)
[end]
[end]
[if-any entries.to_selected]
[if-any entries.prev][else]<br>Diff[end]
to <a href="[href].diff?r1=[rev_selected]&r2=[entries.rev][query]">selected [rev_selected]</a>
[if-any human_readable]
[else]
(<a href="[href].diff?r1=[rev_selected]&r2=[entries.rev]&diff_format=h[query]">colored</a>)
[end]
[end]
[end]
<pre>[entries.html_log]</pre>
[end]
<a name=diff></a>
<hr noshade>
This form allows you to request diffs between any two revisions of
a file. You may select a symbolic revision name using the selection
box or you may type in a numeric name using the type-in text box.
<p>
<form method="GET" action="[href].diff[qquery]" name="diff_select">
Diffs between
<select name="r1">
<option value="text" 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="[tr1]"
onChange="document.diff_select.r1.selectedIndex=0">
and
<select name="r2">
<option value="text" 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="[tr2]"
onChange="document.diff_select.r1.selectedIndex=0">
<br>Type of Diff should be a
<select name="diff_format" onchange="submit()">
<option value="h" [is diff_format "h"]selected[end]>Colored Diff</option>
<option value="l" [is diff_format "l"]selected[end]>Long Colored Diff</option>
<option value="u" [is diff_format "u"]selected[end]>Unidiff</option>
<option value="c" [is diff_format "c"]selected[end]>Context Diff</option>
<option value="s" [is diff_format "s"]selected[end]>Side by Side</option>
</select>
<input type=submit value=" Get Diffs "></form>
<hr noshade>
[if-any branch_names]
<a name=branch></a>
<form method="GET" action="[href]">
[hidden_values]
View only Branch:
<select name="only_with_tag" onchange="submit()">
<option value="" [is view_tag ""]selected[end]>Show all branches</option>
[for branch_names]
<option value="[branch_names]" [is branch_names view_tag]selected[end]>[branch_names]</option>
[end]
</select>
<input type=submit value=" View Branch ">
</form>
[end]
<a name=logsort></a>
<form method="GET" action="[href]">
[hidden_values]
Sort log by:
<select name="logsort" onchange="submit()">
<option value="cvs" [is logsort "cvs"]selected[end]>Not sorted</option>
<option value="date" [is logsort "date"]selected[end]>Commit date</option>
<option value="rev" [is logsort "rev"]selected[end]>Revision</option>
</select>
<input type=submit value=" Sort ">
</form>
[include "footer.ezt"]

251
templates/log_table.ezt Normal file
View File

@@ -0,0 +1,251 @@
<!doctype html public "-//W3C//DTD HTML 4.0 Transitional//EN"
"http://www.w3.org/TR/REC-html40/loose.dtd">
<html><head>
<!-- ViewCVS -- http://viewcvs.sourceforge.net/
by Greg Stein -- mailto:gstein@lyra.org
-->
<title>CVS log for [where]</title>
</head>
<body text="#000000" bgcolor="#ffffff">
<table width="100%" border=0 cellspacing=0 cellpadding=0>
<tr>
<td rowspan=2><h1>CVS log for [where]</h1></td>
<td align=right><img src="/icons/apache_pb.gif" alt="(logo)" border=0
width=259 height=32></td>
</tr>
<tr>
<td align=right><h3><b><a target="_blank"
href="[request.script_name]/*docroot*/help_logtable.html">Help</a></b></h3></td>
</tr>
</table>
<a href="[back_url]"><img src="/icons/small/back.gif" alt="(back)" border=0
width=16 height=16></a>
<b>Up to [nav_path]</b><p>
<a href="#diff">Request diff between arbitrary revisions</a>
[if-any graph_href]
/ <a href="[graph_href]">Display revisions graphically</a>
[end]
<hr noshade>
[if-any branch]
Default branch: [branch]
<br>
Bookmark a link to:
[if-any viewable]
<a href="[head_href]"><b>HEAD</b></a>
/
(<a href="[head_abs_href]" target="cvs_checkout"
onClick="window.open('about:blank', 'cvs_checkout',
'resizeable=1,scrollbars=1[is mime_type "text/html"],status,toolbar[end]')"
><b>download</b></a>)
[else]
<a href="[head_href]" target="cvs_checkout"
onClick="window.open('about:blank', 'cvs_checkout',
'resizeable=1,scrollbars=1[is mime_type "text/html"],status,toolbar[end]')"
><b>HEAD</b></a>
[end]
[else]
No default branch
[end]
<br>
[if-any view_tag]
Current tag: [view_tag] <br>
[end]
<hr noshade>
<table width="100%" border=0 cellspacing=1 cellpadding=2>
<th align=left bgcolor="#88ff88">Revision</th>
<th align=left bgcolor="#88ff88">Tasks</th>
<th align=left bgcolor="#88ff88">Diffs</th>
<th align=left bgcolor="#88ff88">Branches and Tags</th>
<th align=left bgcolor="#88ff88">Age</th>
<th align=left bgcolor="#88ff88">Author</th>
[for entries]
<tr valign="top" bgcolor="[if-index entries even]#ffffff[else]#ccccee[end]">
[# Revision column]
<td rowspan=2>
<b>[entries.rev]</b>
<a name="rev[entries.rev]"></a>
</td>
<td>
[# Tasks column]
<a href="[entries.href]" target="cvs_checkout"
onClick="window.open('about:blank', 'cvs_checkout',
'resizeable=1,menubar=1,scrollbars=1[is mime_type "text/html"],status,
toolbar[end]')"><b>Download</b></a><br>
[is mime_type "text/plain"]
[else]
<a href="[entries.text_href]" target="cvs_checkout"
onClick="window.open('about:blank', 'cvs_checkout',
'resizeable=1,scrollbars=1')"><b>View as text</b></a><br>
[end]
[if-any viewable]
<a href="[entries.view_href]"><b>View [entries.rev]</b></a><br>
[end]
[# if you don't want to allow annotation, then remove this line]
<a href="[href]?annotate=[entries.rev][query]"><b>View annotated</b></a><br>
</td>
<td>
[# if you don't want to allow select for diffs then remove this section]
[is entries.rev rev_selected]
<b>[[]selected]</b><br>
[else]
<a href="[href]?r1=[entries.rev][query]"><b>[[]select&nbsp;for&nbsp;diffs]</b></a><br>
[end]
[is entries.state "dead"]
<b><i>FILE REMOVED</i></b>
[else]
[if-any entries.prev]
<a href="[href].diff?r1=[entries.prev]&r2=[entries.rev][query]"><b>Diff&nbsp;to&nbsp;previous&nbsp;[entries.prev]</b></a><br>
[if-any human_readable]
[else]
<a href="[href].diff?r1=[entries.prev]&r2=[entries.rev]&diff_format=h[query]"><b>colored</b></a><br>
[end]
[end]
[if-any entries.to_selected]
<a href="[href].diff?r1=[rev_selected]&r2=[entries.rev][query]"><b>Diff&nbsp;to&nbsp;selected&nbsp;[rev_selected]</b></a><br>
[if-any human_readable]
[else]
<a href="[href].diff?r1=[rev_selected]&r2=[entries.rev]&diff_format=h[query]"><b>colored</b></a><br>
[end]
[end]
[end]
</td>
<td>
[# Branches column]
[if-any entries.vendor_branch]
<i>vendor branch</i><br>
[end]
[if-any entries.branches]
[for entries.branches]
<a href="[entries.branches.href]"><b>[entries.branches.name]</b></a><br>
[end]
[end]
[if-any entries.branch_points]
Branch point for:
[for entries.branch_points]
<a href="[entries.branch_points.href]"><b>[entries.branch_points.name]</b></a><br>
[end]
[end]
[if-any entries.next_main]
<a href="[href].diff?r1=[entries.next_main]&r2=[entries.rev][query]"><b>Diff&nbsp;to&nbsp;next&nbsp;MAIN&nbsp;[entries.next_main]</b></a><br>
[if-any human_readable]
[else]
<a href="[href].diff?r1=[entries.next_main]&r2=[entries.rev]&diff_format=h[query]"><b>colored</b></a><br>
[end]
[end]
[if-any entries.branch_point]
<a href="[href].diff?r1=[entries.branch_point]&r2=[entries.rev][query]"><b>Diff&nbsp;to&nbsp;branch&nbsp;point&nbsp;[entries.branch_point]</b></a><br>
[if-any human_readable]
[else]
<a href="[href].diff?r1=[entries.branches.name]&r2=[entries.rev]&diff_format=h[query]"><b>colored</b></a><br>
[end]
[end]
[# Tags ]
[if-any entries.tags]
<form action="[href]" >
<select name="only_with_tag" onChange="submit()">
<option value="" [is view_tag ""]selected[end]>Show all tags</option>
[for entries.tags]
<option [is view_tag entries.tags.name]selected[end]>[entries.tags.name]</option>
[end]
</select>
</form>
[else]&nbsp;
[end]
</td>
[# Time column]
<td>
[entries.ago] ago<br><i>[entries.utc_date]</i>
</td>
[# Author column]
<td>
[entries.author]
</td>
</tr>
<tr bgcolor="[if-index entries even]#ffffff[else]#ccccee[end]">
<td colspan=5><b>Log: </b><i><pre>[entries.html_log]</pre></i></td>
</tr>
[end]
</table>
<a name=diff></a>
<hr noshade>
This form allows you to request diffs between any two revisions of
a file. You may select a symbolic revision name using the selection
box or you may type in a numeric name using the type-in text box.
<p>
<form method="GET" action="[href].diff[qquery]" name="diff_select">
Diffs between
<select name="r1">
<option value="text" 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="[tr1]"
onChange="document.diff_select.r1.selectedIndex=0">
and
<select name="r2">
<option value="text" 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="[tr2]"
onChange="document.diff_select.r1.selectedIndex=0">
<br>Type of Diff should be a
<select name="diff_format" onchange="submit()">
<option value="h" [is diff_format "h"]selected[end]>Colored Diff</option>
<option value="l" [is diff_format "l"]selected[end]>Long Colored Diff</option>
<option value="u" [is diff_format "u"]selected[end]>Unidiff</option>
<option value="c" [is diff_format "c"]selected[end]>Context Diff</option>
<option value="s" [is diff_format "s"]selected[end]>Side by Side</option>
</select>
<input type=submit value=" Get Diffs "></form>
<hr noshade>
[if-any branch_names]
<a name=branch></a>
<form method="GET" action="[href]">
[hidden_values]
View only Branch:
<select name="only_with_tag" onchange="submit()">
<option value="" [is view_tag ""]selected[end]>Show all branches</option>
[for branch_names]
<option value="[branch_names]" [is branch_names view_tag]selected[end]>[branch_names]</option>
[end]
</select>
<input type=submit value=" View Branch ">
</form>
[end]
<a name=logsort></a>
<form method="GET" action="[href]">
[hidden_values]
Sort log by:
<select name="logsort" onchange="submit()">
<option value="cvs" [is logsort "cvs"]selected[end]>Not sorted</option>
<option value="date" [is logsort "date"]selected[end]>Commit date</option>
<option value="rev" [is logsort "rev"]selected[end]>Revision</option>
</select>
<input type=submit value=" Sort ">
</form>
[include "footer.ezt"]

49
templates/markup.ezt Normal file
View File

@@ -0,0 +1,49 @@
[include "header.ezt" "view"]
<hr noshade>
<table width="100%"><tr><td bgcolor="#ffffff">
File: [nav_file]
(<a href="[href]" target="cvs_checkout"
onClick="window.open('about:blank','cvs_checkout',
'resizeable=1,scrollbars=1[is mime_type "text/html"],status,toolbar[end]');"
><b>download</b></a>)
[is mime_type "text/plain"]
[else]
/
(<a href="[text_href]" target="cvs_checkout"
onClick="window.open('about:blank', 'cvs_checkout',
'resizeable=1,scrollbars=1')"><b>as text</b></a>)
[end]
<br>
[if-any log]
Revision: <b>[rev]</b>[if-any vendor_branch] <i>(vendor branch)</i>[end],
<i>[utc_date] UTC</i> ([ago] ago) by <i>[author]</i>
[if-any branches]
<br>Branch: <b>[branches]</b>
[end]
[if-any tags]
<br>CVS Tags: <b>[tags]</b>
[end]
[if-any branch_points]
<br>Branch point for: <b>[branch_points]</b>
[end]
[if-any prev]
[if-any changed]
<br>Changes since <b>[prev]: [changed] lines</b>
[end]
[end]
[is state "dead"]
<br><b><i>FILE REMOVED</i></b>
[end]
<pre>[log]</pre>
[else]
Revision: <b>[rev]</b><br>
[if-any tag]
Tag: <b>[tag]</b><br>
[end]
[end]
</td></tr></table>
<hr noshade>

228
templates/query.ezt Normal file
View File

@@ -0,0 +1,228 @@
<!doctype html public "-//W3C//DTD HTML 4.0 Transitional//EN"
"http://www.w3.org/TR/REC-html40/loose.dtd">
<html><head>
<!-- ViewCVS -- http://viewcvs.sourceforge.net/
by Greg Stein -- mailto:gstein@lyra.org
-->
<title>[cfg.general.main_title]</title>
</head>
<body text="#000000" bgcolor="#ffffff">
<table width="100%" border=0 cellspacing=0 cellpadding=0>
<tr>
<td><h1>[cfg.general.main_title]</h1></td>
<td align=right><img src="/icons/apache_pb.gif" alt="(logo)" border=0
width=259 height=32></td>
</tr>
</table>
<p>
Select your parameters for querying the CVS commit database. 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 <i>jpaint</i> and <i>gstein</i>, just type: <b>jpaint,
gstein</b> in the <i>Author</i> 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:
<b>"jpaint", "gstein"</b>.
</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 charactor preceeds the
first quote. The command charactor <b>l</b> is for wildcard
searches, and the wildcard charactor is a percent (<b>%</b>). The
command charactor for regular expressions is <b>r</b>, 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 <i>.py</i> extention is:
<b>l"%.py"</b> in the <i>File</i> input box. The same search done
with a regular expression is: <b>r".*\.py"</b>.
</p>
<p>
All search types can be mixed, as long as they are seperated by
commas.
</p>
<form method=get action="query.cgi">
<table border=0 cellspacing=0 cellpadding=2 width=100% bgcolor=e6e6e6>
<tr>
<td>
<table>
<tr>
<td valign=top>
<table>
<tr>
<td align=right>CVS Repository:</td>
<td>
<input type=text name=repository size=40 value="[repository]">
</td>
</tr>
<tr>
<td align=right>CVS Branch:</td>
<td>
<input type=text name=branch size=40 value="[branch]">
</td>
</tr>
<tr>
<td align=right>Directory:</td>
<td>
<input type=text name=directory size=40 value="[directory]">
</td>
</tr>
<tr>
<td align=right>File:</td>
<td>
<input type=text name=file size=40 value="[file]">
</td>
</tr>
<tr>
<td align=right>Author:</td>
<td>
<input type=text name=who size=40 value="[who]">
</td>
</tr>
</table>
</td>
<td valign=top>
<table>
<tr>
<td align=left>Sort By:</td>
<td>
<select name=sortby>
<option value=date [is sortby "date"]selected[end]>Date</option>
<option value=author [is sortby "author"]selected[end]>Author</option>
<option value=file [is sortby "file"]selected[end]>File</option>
</select>
</td>
</tr>
<tr>
<td colspan=2>
<table border=0 cellspacing=0 cellpadding=0>
<tr>
<td>Date:</td>
</tr>
<tr>
<td><input type=radio name=date value=hours
[is date "hours"]checked[end]></td>
<td>In the last
<input type=text name=hours value=[hours] size=4>hours
</td>
</tr>
<tr>
<td><input type=radio name=date value=day
[is date "day"]checked[end]></td>
<td>In the last day</td>
</tr>
<tr>
<td><input type=radio name=date value=week
[is date "week"]checked[end]></td>
<td>In the last week</td>
</tr>
<tr>
<td><input type=radio name=date value=month
[is date "month"]checked[end]></td>
<td>In the last month</td>
</tr>
<tr>
<td><input type=radio name=date value=all
[is date "all"]checked[end]></td>
<td>Since the beginning of time</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
<td>
<input type=submit value="Search">
</td>
</tr>
</table>
</form>
[is query "skipped"]
[else]
<p><b>[num_commits]</b> matches found.</p>
[if-any commits]
<table width="100%" border=0 cellspacing=0 cellpadding=2>
<tr bgcolor="#88ff88">
<th align=left valign=top>Revision</th>
<th align=left valign=top>File</th>
<th align=left valign=top>Branch</th>
<th align=left valign=top>+/-</th>
<th align=left valign=top>Date</th>
<th align=left valign=top>Author</th>
[# uncommment, if you want a separate Description column: (also see below)
<th align=left valign=top>Description</th>
]
</tr>
[for commits]
[for commits.files]
<tr bgcolor="[if-index commits even]#ffffff[else]#ccccee[end]">
<td align=left valign=top>
[if-any commits.files.rev][commits.files.rev][else]&nbsp;[end]
</td>
<td align=left valign=top>[commits.files.link]</td>
<td align=left valign=top>
[if-any commits.files.branch][commits.files.branch][else]&nbsp;[end]
</td>
<td align=left valign=top>[commits.files.plus]/[commits.files.minus]</td>
<td align=left valign=top>
[if-any commits.files.date][commits.files.date][else]&nbsp;[end]
</td>
<td align=left valign=top>
[if-any commits.files.author][commits.files.author][else]&nbsp;[end]
</td>
[# uncommment, if you want a separate Description column:
{if-index commits.files first{
<td align=left valign=top rowspan={commits.num_files}>
{commits.desc}
</td>
{end}
(substitute brackets for the braces)
]
</tr>
[# and also take the following out in the "Description column"-case:]
[if-index commits.files last]
<tr bgcolor="[if-index commits even]#ffffff[else]#ccccee[end]">
<td>&nbsp;</td>
<td colspan=5><b>Log:</b><br>
<pre>[commits.desc]</pre></td>
</tr>
[end]
[# ---]
[end]
[end]
<tr bgcolor="#88ff88">
<th align=left valign=top>&nbsp;</th>
<th align=left valign=top>&nbsp;</th>
<th align=left valign=top>&nbsp;</th>
<th align=left valign=top>&nbsp;</th>
<th align=left valign=top>&nbsp;</th>
<th align=left valign=top>&nbsp;</th>
<th align=left valign=top>&nbsp;</th>
</tr>
</table>
[end]
[end]
[include "footer.ezt"]

139
tests/timelog.py Normal file
View File

@@ -0,0 +1,139 @@
import time
import string
import profile
import rcsparse
import viewcvs
def lines_changed(delta):
idx = 0
added = deleted = 0
while idx < len(delta):
op = delta[idx]
i = string.find(delta, ' ', idx + 1)
j = string.find(delta, '\n', i + 1)
line = int(delta[idx+1:i])
count = int(delta[i+1:j])
idx = j + 1
if op == 'd':
deleted = deleted + count
else: # 'a' for adding text
added = added + count
# skip new text
while count > 0:
nl = string.find(delta, '\n', idx)
assert nl > 0, 'missing a newline in the delta in the RCS file'
idx = nl + 1
count = count - 1
return added, deleted
class FetchSink(rcsparse.Sink):
def __init__(self, which_rev=None):
self.head = self.branch = ''
self.tags = { }
self.meta = { }
self.revs = [ ]
self.base = { }
self.entries = { }
self.which = which_rev
def set_head_revision(self, revision):
self.head = revision
def set_principal_branch(self, branch_name):
self.branch = branch_name
def define_tag(self, name, revision):
self.tags[name] = revision
def define_revision(self, revision, timestamp, author, state,
branches, next):
self.meta[revision] = (timestamp, author, state)
self.base[next] = revision
for b in branches:
self.base[b] = revision
def set_revision_info(self, revision, log, text):
timestamp, author, state = self.meta[revision]
entry = viewcvs.LogEntry(revision, int(timestamp) - time.timezone, author,
state, None, log)
# .revs is "order seen" and .entries is for random access
self.revs.append(entry)
self.entries[revision] = entry
if revision != self.head:
added, deleted = lines_changed(text)
if string.count(revision, '.') == 1:
# on the trunk. reverse delta.
changed = '+%d -%d' % (deleted, added)
self.entries[self.base[revision]].changed = changed
else:
# on a branch. forward delta.
changed = '+%d -%d' % (added, deleted)
self.entries[revision].changed = changed
def parse_completed(self):
if self.which:
self.revs = [ self.entries[self.which] ]
def fetch_log2(full_name, which_rev=None):
sink = FetchSink(which_rev)
rcsparse.Parser().parse(open(full_name), sink)
return sink.head, sink.branch, sink.tags, sink.revs
def compare_fetch(full_name, which_rev=None):
d1 = viewcvs.fetch_log(full_name, which_rev)
d2 = fetch_log2(full_name, which_rev)
if d1[:3] != d2[:3]:
print 'd1:', d1[:3]
print 'd2:', d2[:3]
return
if len(d1[3]) != len(d2[3]):
print 'len(d1[3])=%d len(d2[3])=%d' % (len(d1[3]), len(d2[3]))
return
def sort_func(e, f):
return cmp(e.rev, f.rev)
d1[3].sort(sort_func)
d2[3].sort(sort_func)
import pprint
for i in range(len(d1[3])):
if vars(d1[3][i]) != vars(d2[3][i]):
pprint.pprint((i, vars(d1[3][i]), vars(d2[3][i])))
def time_fetch(full_name, which_rev=None):
t = time.time()
viewcvs.fetch_log(full_name, which_rev)
t1 = time.time() - t
t = time.time()
fetch_log2(full_name, which_rev)
t2 = time.time() - t
print t1, t2
def profile_fetch(full_name, which_rev=None):
p = profile.Profile()
def many_calls(*args):
for i in xrange(10):
apply(fetch_log2, args)
p.runcall(many_calls, full_name, which_rev)
p.print_stats()
def varysize(full_name, which_rev=None):
def one_run(n, *args):
rcsparse._TokenStream.CHUNK_SIZE = n
t = time.time()
for i in xrange(5):
apply(fetch_log2, args)
print n, time.time() - t
#one_run(2020, full_name, which_rev)
#one_run(4070, full_name, which_rev)
#one_run(8170, full_name, which_rev)
#one_run(8192, full_name, which_rev)
#one_run(16384, full_name, which_rev)
one_run(32740, full_name, which_rev)
one_run(65500, full_name, which_rev)
one_run(100000, full_name, which_rev)
one_run(200000, full_name, which_rev)
one_run(500000, full_name, which_rev)

116
tools/bin2inline_py.py Executable file
View File

@@ -0,0 +1,116 @@
#!/usr/bin/env python
## vim:ts=4:et:nowrap
# [Emacs: -*- python -*-]
"""bin2inline_py.py -- creates Python source from directories containing images.
This is a very simple tool to pack a lot of small icons into a single file.
This version is a quick rape-and-past for the ViewCVS project.
Run this script from within the tools subdirectory to recreate the source file
'../lib/apache_icons.py'.
"""
import sys, os, string, fnmatch
PREAMBLE="""#! /usr/bin/env python
# This file was automatically generated! DO NOT EDIT!
# Howto regenerate: see ../tools/bin2inline.py
# $Id$
# You have been warned. But if you want to edit, go ahead using your
# favorite editor :-)
## vim:ts=4:et:nowrap
# [Emacs: -*- python -*-]
_ap_icons = """
def encodefile(filename):
"""returns the binary content of 'filename' as string"""
s = open(filename, 'rb').read()
result = [ ]
while s:
result.append(repr(s[:16]))
s = s[16:]
return string.join(result, '\n ')
class Encode:
"""Starting at a given directory find all files matching a certain
filename pattern in this subtree, encode them as base64 strings and
return a Python language dictionary with the filenames as keys and
the files contents as values.
"""
def __init__(self, startdir=os.curdir, fn_pattern="*.gif"):
self.startdir = startdir
self.fn_pattern = fn_pattern
self.walk()
def walk(self):
"""walk through the subtree starting at self.startdir"""
self.result = ['{\n']
os.path.walk(self.startdir, self.visitor, None)
self.result.append('}\n')
def visitor(self, dummy, dirname, filenames):
"""A visitor compatible with os.path.walk()."""
for candidate in filenames:
pathname = os.path.join(dirname, candidate)
if not os.path.isfile(pathname):
continue
if self.match(pathname):
self.put_item(pathname)
def match(self, candidate):
"""should return false, if pathname 'candidate' should be skipped.
"""
return fnmatch.fnmatch(candidate, self.fn_pattern)
def put_item(self, pathname):
self.result.append(' "%s" :\n %s,\n\n'
% (self.compute_key(pathname),
encodefile(pathname)))
def compute_key(self, pathname):
"""computes the dictionary key. Tkinter compatible"""
return os.path.splitext(pathname)[0]
def __str__(self):
return string.join(self.result, '')
#
# The remainder of this module is best described as hack, run and throw away
# software. You may want to edit it, if you want to other icons.
#
class WebserverIconEncode(Encode):
minimal_list = [ # List of icons actually used by ViewCVS as of 2001-11-17
"/icons/apache_pb.gif",
"/icons/small/back.gif",
"/icons/small/dir.gif",
"/icons/small/text.gif",
]
def match(self, candidate):
return self.compute_key(candidate) in self.minimal_list
def compute_key(self, pathname):
l = len(self.startdir)
if pathname[:l] == self.startdir:
return pathname[l:]
return pathname
POSTAMBLE="""
def serve_icon(pathname, fp):
if _ap_icons.has_key(pathname):
fp.write(_ap_icons[pathname])
else:
raise OSError # icon not found
"""
if __name__ == "__main__":
import sys
f = open("../lib/apache_icons.py", "wt")
f.write(PREAMBLE)
f.write(str(WebserverIconEncode(startdir="/usr/local/httpd")))
f.write(POSTAMBLE)

163
tools/cvsdbadmin Executable file
View File

@@ -0,0 +1,163 @@
#!/usr/bin/env python
# -*- Mode: python -*-
#
# Copyright (C) 2000 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 ViewCVS
# distribution or at http://www.lyra.org/viewcvs/license-1.html.
#
# Contact information:
# Greg Stein, PO Box 760, Palo Alto, CA, 94302
# gstein@lyra.org, http://www.lyra.org/viewcvs/
#
# -----------------------------------------------------------------------
#
# administrative program for CVSdb; this is primarily
# used to add/rebuild CVS repositories to the database
#
# -----------------------------------------------------------------------
#
#########################################################################
#
# INSTALL-TIME CONFIGURATION
#
# These values will be set during the installation process. During
# development, they will remain None.
#
LIBRARY_DIR = None
CONF_PATHNAME = None
# Adjust sys.path to include our library directory
import sys
if LIBRARY_DIR:
sys.path.insert(0, LIBRARY_DIR)
else:
sys.path[:0] = ['../lib'] # any other places to look?
#########################################################################
import os
import string
import cvsdb
def UpdateFile(db, repository, path):
try:
commit_list = cvsdb.GetUnrecordedCommitList(repository, path)
except cvsdb.error, e:
print '[ERROR] %s' % (e)
return
print '[%s[%d new commits]]' % (path, len(commit_list)),
## add the commits into the database
for commit in commit_list:
db.AddCommit(commit)
sys.stdout.write('.')
sys.stdout.flush()
print
def RecurseUpdate(db, repository, directory):
for path in os.listdir(directory):
path = os.path.join(directory, path)
if os.path.islink(path):
continue
if os.path.isdir(path):
RecurseUpdate(db, repository, path)
continue
if os.path.isfile(path):
if path[-2:] == ',v':
UpdateFile(db, repository, path)
def CommandUpdate():
## connect to the database we are updating
db = cvsdb.ConnectDatabase()
repository = sys.argv[2]
RecurseUpdate(db, repository, repository)
def RebuildFile(db, repository, path):
try:
commit_list = cvsdb.GetCommitListFromRCSFile(repository, path)
except cvsdb.error, e:
print '[ERROR] %s' % (e)
return
print '[%s[%d commits]]' % (path, len(commit_list)),
## add the commits into the database
for commit in commit_list:
db.AddCommit(commit)
sys.stdout.write('.')
sys.stdout.flush()
print
def RecurseRebuild(db, repository, directory):
for path in os.listdir(directory):
path = os.path.join(directory, path)
if os.path.islink(path):
continue
if os.path.isdir(path):
RecurseRebuild(db, repository, path)
continue
if os.path.isfile(path):
if path[-2:] == ',v':
RebuildFile(db, repository, path)
def CommandRebuild():
## connect to the database we are updating
db = cvsdb.ConnectDatabase()
repository = sys.argv[2]
RecurseRebuild(db, repository, repository)
def usage():
print 'Usage: %s <command> [arguments]' % (sys.argv[0])
print 'Preforms administrative functions for the CVSdb database'
print 'Commands:'
print ' rebuild <repository> rebuilds the CVSdb database'
print ' for all files in the repository'
print ' update <repository> updates the CVSdb database for'
print ' all unrecorded commits'
print
sys.exit(1)
## main
if __name__ == '__main__':
## check that a command was given
if len(sys.argv) < 2:
usage()
## set the handler function for the command
command = sys.argv[1]
if string.lower(command) == 'rebuild':
commandFunction = CommandRebuild
elif string.lower(command) == 'update':
commandFunction = CommandUpdate
else:
print 'ERROR: unknown command %s' % (command)
usage()
## run command
try:
commandFunction()
except KeyboardInterrupt:
print
print '** break **'
sys.exit(0)

190
tools/loginfo-handler Executable file
View File

@@ -0,0 +1,190 @@
#!/usr/bin/python
# -*- Mode: python -*-
#
# Copyright (C) 2000 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 ViewCVS
# distribution or at http://www.lyra.org/viewcvs/license-1.html.
#
# Contact information:
# Greg Stein, PO Box 760, Palo Alto, CA, 94302
# gstein@lyra.org, http://www.lyra.org/viewcvs/
#
# -----------------------------------------------------------------------
#
# updates SQL database with new commit records
#
# -----------------------------------------------------------------------
#
#########################################################################
#
# INSTALL-TIME CONFIGURATION
#
# These values will be set during the installation process. During
# development, they will remain None.
#
LIBRARY_DIR = None
CONF_PATHNAME = None
# Adjust sys.path to include our library directory
import sys
if LIBRARY_DIR:
sys.path.insert(0, LIBRARY_DIR)
else:
sys.path[:0] = ['../lib'] # any other places to look?
#########################################################################
import os
import string
import getopt
import re
import cvsdb
import rlog
import config
DEBUG_FLAG = 0
## pre-compiled regular expressions
_re_fileversion = re.compile("([^,]+)\,([^,]+)\,([^,]+)")
## output functions
def debug(text):
if DEBUG_FLAG:
print 'DEBUG(loginfo): %s' % (text)
def warning(text):
print 'WARNING(loginfo): %s' % (text)
def error(text):
print 'ERROR(loginfo): %s' % (text)
sys.exit(1)
class FileData:
def __init__(self, file, directory, old_version, new_version):
self.file = file
self.directory = directory
self.old_version = old_version
self.new_version = new_version
## set the state of this file from the
## old_version and new_version information
if self.old_version == 'NONE':
self.ctype = "added"
elif self.new_version == 'NONE':
self.ctype = "removed"
else:
self.ctype = "changed"
def CommitFromFileData(cfg, repository, file_data):
## construct the full path for the RCS file
filename = os.path.join(repository, file_data.directory, file_data.file)
## get the 'rlog' output for just this revision, and then convert
## to a commit object
rlog_data = rlog.GetRLogData(cfg, filename, file_data.new_version)
commit_list = cvsdb.RLogDataToCommitList(repository, rlog_data)
commit = commit_list[0]
## set the type of commit from the file_data setting
if file_data.ctype == "changed":
commit.SetTypeChange()
elif file_data.ctype == "added":
commit.SetTypeAdd()
elif file_data.ctype == "removed":
commit.SetTypeRemove()
return commit
def GetUnrecordedCommitList(repository, file_data):
filename = os.path.join(repository, file_data.directory, file_data.file)
return cvsdb.GetUnrecordedCommitList(repository, filename)
def ProcessLoginfo(repository, stdin_list):
## XXX This is a somewhat dirty hack:
## cvsdb already loads the configuration file and provides a cfg
## instance. Pick it from there in order to be able to pass it down
## to rlog (see below).
cfg = cvsdb.cfg
## the first line in stdin is a space-separated list; the first
## item in the list is the directory path being updated this run;
## the rest of the items are the files being updated
list = string.split(stdin_list[0])
## clean up the directory the following way: we don't want it
## to begin with a path seperator, and we don't want it to end
## with a path seperator
directory = list[0]
while directory[0] == os.sep:
directory = directory[1:]
while directory[-1] == os.sep:
directory = directory[:-1]
## NOTE: SPECIAL HANDLING FOR NEW DIRECTORIES
## new directories have the first line form
## path/of/dir - New directory
if len(list) == 4:
if list[1] == '-' and list[2] == 'New' and list[3] == 'directory':
debug('new directory')
return
## each file in the file list _should_ be of the form:
## file-name,<old-ver>,<new-ver>
## a new file has the keyword 'NONE' for old-ver
file_data_list = []
for item in list[1:]:
temp = _re_fileversion.match(item)
if not temp:
debug('failed match %s' % (item))
continue
filename = temp.group(1)
old_version = temp.group(2)
new_version = temp.group(3)
file_data = FileData(filename, directory, old_version, new_version)
file_data_list.append(file_data)
## convert FileData objects into Commit objects so we can insert them
## into the database
commit_list = []
for file_data in file_data_list:
## XXX: this is nasty: in the case of a removed file, we are not
## given enough information to find it in the rlog output!
## So instead, we rlog everything in the removed file, and
## add any commits not already in the database
if file_data.ctype == "removed":
temp = GetUnrecordedCommitList(repository, file_data)
commit_list = commit_list + temp
else:
commit_list.append(CommitFromFileData(cfg, repository, file_data))
## add to the database
db = cvsdb.ConnectDatabase()
db.AddCommitList(commit_list)
## MAIN
if __name__ == '__main__':
## get the repository from the environment
try:
repository = os.environ['CVSROOT']
except KeyError:
error('CVSROOT not in environment')
## clean up the repository string: remove any trailing path seperater
while repository[-1] == os.sep:
repository = repository[:-1]
## read all the lines from stdin
stdin_list = []
for line in sys.stdin.readlines():
stdin_list.append(string.rstrip(line))
ProcessLoginfo(repository, stdin_list)
sys.exit(0)

150
tools/make-database Executable file
View File

@@ -0,0 +1,150 @@
#!/usr/bin/env python
# -*- Mode: python -*-
#
# Copyright (C) 2000-2001 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 ViewCVS
# distribution or at http://viewcvs.sourceforge.net/license-1.html.
#
# Contact information:
# Greg Stein, PO Box 760, Palo Alto, CA, 94302
# gstein@lyra.org, http://viewcvs.sourceforge.net/
#
# -----------------------------------------------------------------------
#
# administrative program for CVSdb; creates a clean database in
# MySQL 3.22 or later
#
# -----------------------------------------------------------------------
#
import os, sys, string
import popen2
INTRO_TEXT = """\
This script creates the database and tables in MySQL used by the ViewCVS
checkin database. You will be prompted for: database user, database user
password, and database name. This script will use mysql to create the
database for you. You will then need to set the appropriate parameters
in your viewcvs.conf file under the [cvsdb] section.
"""
DATABASE_SCRIPT="""\
DROP DATABASE IF EXISTS <dbname>;
CREATE DATABASE <dbname>;
USE <dbname>;
DROP TABLE IF EXISTS branches;
CREATE TABLE branches (
id mediumint(9) DEFAULT '0' NOT NULL auto_increment,
branch varchar(64) binary DEFAULT '' NOT NULL,
PRIMARY KEY (id),
UNIQUE branch (branch)
);
DROP TABLE IF EXISTS checkins;
CREATE TABLE checkins (
type enum('Change','Add','Remove'),
ci_when datetime DEFAULT '0000-00-00 00:00:00' NOT NULL,
whoid mediumint(9) DEFAULT '0' NOT NULL,
repositoryid mediumint(9) DEFAULT '0' NOT NULL,
dirid mediumint(9) DEFAULT '0' NOT NULL,
fileid mediumint(9) DEFAULT '0' NOT NULL,
revision varchar(32) binary DEFAULT '' NOT NULL,
stickytag varchar(255) binary DEFAULT '' NOT NULL,
branchid mediumint(9) DEFAULT '0' NOT NULL,
addedlines int(11) DEFAULT '0' NOT NULL,
removedlines int(11) DEFAULT '0' NOT NULL,
descid mediumint(9),
UNIQUE repositoryid (repositoryid,dirid,fileid,revision),
KEY ci_when (ci_when),
KEY whoid (whoid),
KEY repositoryid_2 (repositoryid),
KEY dirid (dirid),
KEY fileid (fileid),
KEY branchid (branchid)
);
DROP TABLE IF EXISTS descs;
CREATE TABLE descs (
id mediumint(9) DEFAULT '0' NOT NULL auto_increment,
description text,
hash bigint(20) DEFAULT '0' NOT NULL,
PRIMARY KEY (id),
KEY hash (hash)
);
DROP TABLE IF EXISTS dirs;
CREATE TABLE dirs (
id mediumint(9) DEFAULT '0' NOT NULL auto_increment,
dir varchar(128) binary DEFAULT '' NOT NULL,
PRIMARY KEY (id),
UNIQUE dir (dir)
);
DROP TABLE IF EXISTS files;
CREATE TABLE files (
id mediumint(9) DEFAULT '0' NOT NULL auto_increment,
file varchar(128) binary DEFAULT '' NOT NULL,
PRIMARY KEY (id),
UNIQUE file (file)
);
DROP TABLE IF EXISTS people;
CREATE TABLE people (
id mediumint(9) DEFAULT '0' NOT NULL auto_increment,
who varchar(32) binary DEFAULT '' NOT NULL,
PRIMARY KEY (id),
UNIQUE who (who)
);
DROP TABLE IF EXISTS repositories;
CREATE TABLE repositories (
id mediumint(9) DEFAULT '0' NOT NULL auto_increment,
repository varchar(64) binary DEFAULT '' NOT NULL,
PRIMARY KEY (id),
UNIQUE repository (repository)
);
DROP TABLE IF EXISTS tags;
CREATE TABLE tags (
repositoryid mediumint(9) DEFAULT '0' NOT NULL,
branchid mediumint(9) DEFAULT '0' NOT NULL,
dirid mediumint(9) DEFAULT '0' NOT NULL,
fileid mediumint(9) DEFAULT '0' NOT NULL,
revision varchar(32) binary DEFAULT '' NOT NULL,
UNIQUE repositoryid (repositoryid,dirid,fileid,branchid,revision),
KEY repositoryid_2 (repositoryid),
KEY dirid (dirid),
KEY fileid (fileid),
KEY branchid (branchid)
);
"""
if __name__ == "__main__":
print INTRO_TEXT
user = raw_input("MySQL User: ")
passwd = raw_input("MySQL Password: ")
dbase = raw_input("ViewCVS Database Name [default: ViewCVS]: ")
if not dbase:
dbase = "ViewCVS"
cmd = "{ mysql --user=%s --password=%s ; } 2>&1" % (user, passwd)
dscript = string.replace(DATABASE_SCRIPT, "<dbname>", dbase)
pipes = popen2.Popen3(cmd)
pipes.tochild.write(dscript)
pipes.tochild.close()
print pipes.fromchild.read()
status = pipes.wait()
if status:
print "[ERROR] the database did not create sucessfully."
sys.exit(1)
print "Database created successfully."
sys.exit(0)

53
tools/make-release Executable file
View File

@@ -0,0 +1,53 @@
#!/bin/sh
#
# Copyright (C) 2000-2001 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 ViewCVS
# distribution or at http://viewcvs.sourceforge.net/license-1.html.
#
# Contact information:
# Greg Stein, PO Box 760, Palo Alto, CA, 94302
# gstein@lyra.org, http://viewcvs.sourceforge.net/
#
# -----------------------------------------------------------------------
#
# make-release: internal tool for creating ViewCVS releases
#
# -----------------------------------------------------------------------
#
if test $# != 2; then
echo "USAGE: $0 tagname target-directory"
exit 1
fi
if test -e $2; then
echo "ERROR: must remove $2 first."
exit 1
fi
# grab a copy of the CVS repository
echo 'Checking out into:' $2
cvs -d :pserver:anonymous@cvs.viewcvs.sourceforge.net:/cvsroot/viewcvs export -r $1 -d $2 viewcvs
# various shifting, cleanup.
# documentation is now also distributed together with the release,
# but we still copy the license file to its traditional place (it is small
# and many files still contain comments refering to this location):
cp $2/website/license-1.html $2/LICENSE.html
# rm -r $2/website
# remove some tools only useful for ViewCVS developers:
rm $2/tools/make-release
rm -f $2/tools/bin2inline_py.py
# Make sure, permissions are reasonable:
find $2 -print | xargs chmod uoa+r
find $2 -type d -print | xargs chmod uoa+x
# cut the tarball:
tar cf - $2 | gzip -9 > $2.tar.gz
# create also a ZIP file for those poor souls :-) still using Windows:
zip -qor9 $2.zip $2
echo 'Done.'

262
viewcvs-install Executable file
View File

@@ -0,0 +1,262 @@
#!/usr/bin/env python
# -*- Mode: python -*-
#
# Copyright (C) 2000-2001 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 ViewCVS
# distribution or at http://viewcvs.sourceforge.net/license-1.html.
#
# Contact information:
# Greg Stein, PO Box 760, Palo Alto, CA, 94302
# gstein@lyra.org, http://viewcvs.sourceforge.net/
#
# -----------------------------------------------------------------------
#
# install script for viewcvs -- temporary?
#
# ### this will eventually be replaced by autoconf plus tools. an
# ### interactive front-end to ./configure may be provided.
#
# -----------------------------------------------------------------------
#
import os
import sys
import string
import re
import traceback
import py_compile
import StringIO
# get access to our library modules
sys.path.insert(0, os.path.join(os.path.dirname(sys.argv[0]), 'lib'))
import compat
import viewcvs
import ndiff
version = viewcvs.__version__
## installer text
INFO_TEXT = """\
This is the ViewCVS %s installer.
It will allow you to choose the install path for ViewCVS. You will
now be asked some installation questions.
Defaults are given in square brackets. Just hit [Enter] if a default
is okay.
""" % version
## installer defaults
ROOT_DIR = "/usr/local/viewcvs-" + version
## list of files for installation
## tuple (source path, destination path, install mode, true/false flag for
## search-and-replace, flag or text for prompt before replace,
## compile_it)
##
FILE_INFO_LIST = [
("cgi/viewcvs.cgi", "cgi/viewcvs.cgi", 0755, 1, 0, 0),
("cgi/query.cgi", "cgi/query.cgi", 0755, 1, 0, 0),
("standalone.py", "standalone.py", 0755, 1, 0, 0),
("cgi/viewcvs.conf.dist", "viewcvs.conf", 0644, 1,
"""Note: If you are upgrading from viewcvs-0.7 or earlier:
The section [text] has been removed from viewcvs.conf. The functionality
went into the new files in subdirectory templates.""", 0),
("cgi/cvsgraph.conf.dist", "cvsgraph.conf", 0644, 0, 1, 0),
("lib/PyFontify.py", "lib/PyFontify.py", 0644, 0, 0, 1),
("lib/blame.py", "lib/blame.py", 0644, 0, 0, 1),
("lib/compat.py", "lib/compat.py", 0644, 0, 0, 1),
("lib/config.py", "lib/config.py", 0644, 0, 0, 1),
("lib/cvsdb.py", "lib/cvsdb.py", 0644, 1, 0, 1),
("lib/dbi.py", "lib/dbi.py", 0644, 0, 0, 1),
("lib/debug.py", "lib/debug.py", 0644, 0, 0, 1),
("lib/popen.py", "lib/popen.py", 0644, 0, 0, 1),
("lib/py2html.py", "lib/py2html.py", 0644, 0, 0, 1),
("lib/query.py", "lib/query.py", 0644, 1, 0, 1),
("lib/rcsparse.py", "lib/rcsparse.py", 0644, 0, 0, 1),
("lib/rlog.py", "lib/rlog.py", 0644, 0, 0, 1),
("lib/viewcvs.py", "lib/viewcvs.py", 0644, 1, 0, 1),
("lib/ezt.py", "lib/ezt.py", 0644, 0, 0, 1),
("lib/apache_icons.py", "lib/apache_icons.py", 0644, 0, 0, 1),
("lib/accept.py", "lib/accept.py", 0644, 0, 0, 1),
("templates/annotate.ezt", "templates/annotate.ezt", 0644, 0, 1, 0),
("templates/diff.ezt", "templates/diff.ezt", 0644, 0, 1, 0),
("templates/directory.ezt", "templates/directory.ezt", 0644, 0, 1, 0),
("templates/dir_alternate.ezt", "templates/dir_alternate.ezt", 0644, 0, 1, 0),
("templates/footer.ezt", "templates/footer.ezt", 0644, 0, 1, 0),
("templates/graph.ezt", "templates/graph.ezt", 0644, 0, 1, 0),
("templates/header.ezt", "templates/header.ezt", 0644, 0, 1, 0),
("templates/log.ezt", "templates/log.ezt", 0644, 0, 1, 0),
("templates/log_table.ezt", "templates/log_table.ezt", 0644, 0, 1, 0),
("templates/markup.ezt", "templates/markup.ezt", 0644, 0, 1, 0),
("templates/query.ezt", "templates/query.ezt", 0644, 0, 1, 0),
("tools/loginfo-handler", "loginfo-handler", 0755, 1, 0, 0),
("tools/cvsdbadmin", "cvsdbadmin", 0755, 1, 0, 0),
("tools/make-database", "make-database", 0755, 1, 0, 0),
("website/help_rootview.html", "doc/help_rootview.html", 0644, 0, 0, 0),
("website/help_dirview.html", "doc/help_dirview.html", 0644, 0, 0, 0),
("website/help_query.html", "doc/help_query.html", 0644, 0, 0, 0),
("website/help_log.html", "doc/help_log.html", 0644, 0, 0, 0),
("website/help_logtable.html", "doc/help_logtable.html", 0644, 0, 0, 0),
("website/images/logo.png", "doc/images/logo.png", 0644, 0, 0, 0),
("website/images/chalk.jpg", "doc/images/chalk.jpg", 0644, 0, 0, 0),
("website/images/cvsgraph_16x16.png", "doc/images/cvsgraph_16x16.png", 0644, 0, 0, 0),
("website/images/cvsgraph_32x32.png", "doc/images/cvsgraph_32x32.png", 0644, 0, 0, 0),
]
def Error(text, etype=None, evalue=None):
print
print "[ERROR] %s" % text
if etype:
print '[ERROR] ',
traceback.print_exception(etype, evalue, None, file=sys.stdout)
sys.exit(1)
def MkDir(path):
try:
compat.makedirs(path)
except os.error, e:
if e[0] == 17:
# EEXIST: file exists
return
if e[0] == 13:
# EACCES: permission denied
Error("You do not have permission to create directory %s" % path)
Error("Unknown error creating directory %s" % path, OSError, e)
def SetOnePath(contents, var, value):
pattern = re.compile('^' + var + r'\s*=\s*.*$', re.MULTILINE)
repl = '%s = "%s"' % (var, os.path.join(ROOT_DIR, value))
return re.sub(pattern, repl, contents)
def SetPythonPaths(contents):
if contents[:2] == '#!':
shbang = '#!' + sys.executable
contents = re.sub('^#![^\n]*', shbang, contents)
contents = re.sub("<VIEWCVS_INSTALL_DIRECTORY>", ROOT_DIR, contents)
contents = SetOnePath(contents, 'LIBRARY_DIR', 'lib')
contents = SetOnePath(contents, 'CONF_PATHNAME', 'viewcvs.conf')
return contents
def InstallFile(src_path, dest_path, mode, set_python_paths, prompt_replace,
compile_it):
dest_path = os.path.join(ROOT_DIR, dest_path)
if prompt_replace and os.path.exists(dest_path):
# Collect ndiff output from ndiff
sys.stdout = StringIO.StringIO()
ndiff.main([dest_path,src_path])
ndiff_output = sys.stdout.getvalue()
# Return everything to normal
sys.stdout = sys.__stdout__
# Collect the '+ ' and '- ' lines
# total collects the difference lines to be printed later
total = ""
# I use flag to throw out match lines.
flag = 1
for line in string.split(ndiff_output,'\n'):
# Print line if it is a difference line
if line[:2] == "+ " or line[:2] == "- " or line[:2] == "? ":
total = total + line + "\n"
flag = 1
else:
# Compress lines that are the same to print one blank line
if flag:
total = total + "\n"
flag = 0
if total == "\n":
print " File %s exists,\n but there is no difference between target and source files.\n" % (dest_path)
return
if type(prompt_replace) == type(""):
print prompt_replace
while 1:
temp = raw_input("\n File %s\n exists and is different from source file.\n DO YOU WANT TO,\n overwrite [o]\n do not overwrite [d]\n view differences [v]: " % (dest_path))
print
temp = string.lower(temp[0])
if temp == "d":
return
if temp == "v":
print total
print "\nLEGEND\n A leading '- ' indicates line to remove from installed file\n A leading '+ ' indicates line to add to installed file\n A leading '? ' shows intraline differences."
if temp == "o":
ReplaceFile(src_path, dest_path, mode, set_python_paths, prompt_replace, compile_it)
return
else:
ReplaceFile(src_path, dest_path, mode, set_python_paths, prompt_replace, compile_it)
return
def ReplaceFile(src_path, dest_path, mode, set_python_paths, prompt_replace, compile_it):
try:
contents = open(src_path, "r").read()
except IOError, e:
Error(str(e))
if set_python_paths:
contents = SetPythonPaths(contents)
## write the file to the destination location
path, basename = os.path.split(dest_path)
MkDir(path)
try:
open(dest_path, "w").write(contents)
except IOError, e:
if e[0] == 13:
# EACCES: permission denied
Error("You do not have permission to write file %s" % dest_path)
Error("Unknown error writing file %s" % dest_path, IOError, e)
os.chmod(dest_path, mode)
if compile_it:
py_compile.compile(dest_path)
return
## MAIN
if __name__ == "__main__":
print INFO_TEXT
## get the install path
temp = raw_input("Installation Path [%s]: " % ROOT_DIR)
temp = string.strip(temp)
if len(temp):
ROOT_DIR = temp
## install the files
print
print "Installing ViewCVS to:", ROOT_DIR
for args in FILE_INFO_LIST:
print " ", args[0]
apply(InstallFile, args)
print
print "Installation Complete"

173
website/contributing.html Normal file
View File

@@ -0,0 +1,173 @@
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<title>Contributing to ViewCVS Development</title>
</head>
<body background="images/chalk.jpg">
<table width="100&#37;" cellspacing=5>
<tr>
<td width="1%"><a href="index.html"><img border=0
src="images/logo.png"></a>
</td>
<td>
<h1>Contributing to ViewCVS Development</h1>
</td>
<td width="1%"><a href="http://sourceforge.net/"><img border=0
src="http://sourceforge.net/sflogo.php?group_id=18760&type=1"></a><br><a href="http://sourceforge.net/projects/viewcvs/">ViewCVS&nbsp;project&nbsp;page</a>
</td>
</tr>
<tr><td width="1%" valign=top>
<a href="index.html">Home</a><br>
<a href="upgrading.html">Upgrading</a><br>
Contributing<br>
<a href="license-1.html">License</a><br>
<a href="who.html">Who</a><br>
</td><td colspan=2>
<p>
Contributions to ViewCVS are very welcome.
</p>
<h2>Getting Started</h2>
<p>
Some basic knowledge about <a href="http://www.python.org"><i>Python</i></a> and
development Tools like <code>diff</code> is required. Best is you start
with a fresh snapshot, which you may obtain from the
<a href="http://sourceforge.net/cvs/?group_id=18760">CVS-Repository</a>
at SourceForge.
</p>
<h2>Testing and reporting</h2>
<p>
Testing usability and the installation process on different platforms is
also a valuable contribution. Please report your results back to
us developers. But always tell us, which version of ViewCVS
you used on which platform.
</p><p>
If you are using the latest current development version please note
the date of your latest download or cvs update when reporting.
</p>
<h2>Does a Certain Coding Style Guide apply?</h2>
<p>
Maintain whatever style is present in the code being modified. New
code can use anything sane (which generally means
<a href="http://python.sourceforge.net/peps/pep-0008.html"><i>PEP&nbsp;8</i></a>).
Greg's only real peeve is if someone writes a function call as:
<code>some_func&nbsp;(args)</code> -- that space is Huge Badness.
Otherwise... <i>shrug</i>.
</p>
<h2>Submitting patches</h2>
<p>
Please use the
<a href="http://sourceforge.net/tracker/?func=add&group_id=18760&atid=318760">SourceForge&nbsp;project&nbsp;patch&nbsp;manager</a>
to submit your patches. Unified context diffs relative to the latest
development version are preferred.
</p><p>
If you have commit access, then you should know what
you're doing. Just make changes directly. Subscribing to
the <a href="http://mailman.lyra.org/mailman/listinfo/viewcvs-dev"><i>developer&nbsp;mailing&nbsp;list</i></a>
is recommended in any case.
</p>
<h2>Preserving security</h2>
<p>
Since ViewCVS is used in the internet, security is a major issue.
If you need to pass data from the request into an external program,
please don't use <code>os.system()</code> or <code>os.popen()</code>.
Please use the module <code>lib/popen</code> that comes with ViewCVS
instead.
</p>
<h2>Adding new features</h2>
<p>
If a new file or module is added, a new line in the installer program
<code>viewcvs-install</code> is required.
</p><p>
The library subdirectory contains a module <code>debug</code>, which may
useful to make performance tests.
</p><p>
If you need a new configuration option think carefully, into
which section it belongs. Try to keep the content of
<code>cgi/viewcvs.conf.dist</code> file and the library module
<code>lib/config.py</code> in sync.
</p>
<h2>Hacking on templates</h2>
<p>
The library module <code>ezt</code> contains a module doc string which
describes the directives used in the HTML templates, which can be found
in the <code>templates</code> sub directory.
</p>
<h2>Cutting a release</h2>
<p>
Also there actually is a script <code>tools/make-release</code>,
which creates a release directory, all other steps required to get
a ViewCVS release out of the door will be currently executed manually
by Greg Stein.
</p><p>
Nevertheless in case he ever wants to retire from this job, it is
probably a good idea to write the procedure down here:
<ol>
<li>Add a new subsection to the file
<code>website/upgrading.html</code> describing all user visible
changes for users of previous releases of ViewCVS.
<li>Test, Test, Test! At the time of this writing (0.8-dev) there
is no automatic testsuite available. So just run with permuting
different <code>viewcvs.conf</code> settings and ... pray.
<li>Review any <a
href="http://sourceforge.net/tracker/?atid=118760&group_id=18760&func=browse">
bug reports, that are still marked open.</a>
<li>Edit the file <code>lib/viewcvs.py</code> and remove the
<tt>"-dev"</tt> suffix from <code>__version__</code>. The remainder
should be of the form X.Y, where X is a positive number and
Y is a single digit.
<li>commit this changed version of <code>lib/viewcvs.py</code>
and than <code>cvs tag V</code><i>X</i><code>_</code><i>Y</i>, where
<i>X</i> and <i>Y</i> should be replaced by the release number
from above. If a developer is willing to volunteer as a
bug fix patch release manager, it is now possible to start here
at this point with a feature freezed branch using the command
<blockquote>
<code>cvs tag -b V</code><i>X</i><code>_</code><i>Y</i><code>_maint</code>
</blockquote>
<li>go into an empty directory and run the command:
<blockquote>
<code>tools/make-release V</code><i>X_Y</i> <code>viewcvs-</code><i>X.Y</i>
</blockquote>
This step requires anonymous CVS access to repository at SourceForge.
<li>pack the content of this <code>viewcvs-</code><i>X.Y</i> directory
into a tarball.
<li>Upload the created tarball into the download files section of the
ViewCVS project at SourceForge.
<br><b>Greg:</b><i>Could you please elaborate this step?</i>
<li>Edit the file <code>lib/viewcvs.py</code> again and this time
increment the <code>__version__</code> for the next release cycle,
again append the <code>"-dev"</code> to the version and again
<blockquote>
<code>cvs commit -m "new release cycle begins" lib/viewcvs.py</code>.
</blockquote>
<li>Write an announcement explaining all the cool new features
and put it out to:
<ul>
<li><a href="http://www.freshmeat.net"><i>www.freshmeat.net</i></a>
<li><a href="http://www.vex.net/parnassus/apyllo.py?i=91022454"><i>Vaults of Parnassus</i></a>
<li><a href="news:comp.lang.python">comp.lang.python</a>
<li><i>Where else? I dunno. Suggestions welcome.</i>
</ul>
</ol>
</td></tr>
</table>
<hr>
<address><a href="mailto:viewcvs-dev@lyra.org">ViewCVS Group</a></address>
<!-- Created: Thu Oct 18 09:05:40 CEST 2001 -->
<!-- hhmts start -->
Last modified: Mon Nov 19 20:25:38 CEST 2001
<!-- hhmts end -->
</body>
</html>

53
website/help_dirview.html Normal file
View File

@@ -0,0 +1,53 @@
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<title>ViewCVS Help: Directory View</title>
</head>
<body background="images/chalk.jpg">
<table width="100&#37;" cellspacing=5>
<tr>
<td width="1%"><a href="http://viewcvs.sf.net/index.html"><img border=0
src="images/logo.png"></a>
</td>
<td>
<h1>ViewCVS Help: Directory View</h1>
</td>
</tr>
<tr><td width="1%" valign=top bgcolor="#ffffff">
<h3>Help</h3>
<a href="help_rootview.html">General</a><br>
<b>Directory&nbsp;View</b><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>
<a href="help_query.html">Query&nbsp;Database</a><br>
<h3>Internet</h3>
<a href="http://viewcvs.sf.net/index.html">Home</a><br>
<a href="http://viewcvs.sf.net/upgrading.html">Upgrading</a><br>
<a href="http://viewcvs.sf.net/contributing.html">Contributing</a><br>
<a href="http://viewcvs.sf.net/license-1.html">License</a><br>
</td><td colspan=2>
<p>
Click on a directory to enter that directory. Click in the leftmost
column of a row to display the revision history of a file and to get
a chance to display diffs between revisions. Click on second column
to view the content of the latest revision of that file.
</p>
<p>
Directories are always displayed first in alphabetically order.
Ordinary files follow and are sorted according to the selected
sort criteria. You may click on a particular column header to
select this column as sort criteria. It is than displayed in
another color (e.g. light green, if using the default configuration).
</p>
</td></tr></table>
<hr>
<address><a href="mailto:viewcvs-dev@lyra.org">ViewCVS Group</a></address>
<!-- Created: Thu Oct 25 22:08:29 CEST 2001 -->
<!-- hhmts start -->
Last modified: Tue Nov 13 20:40:02 CEST 2001
<!-- hhmts end -->
</body>
</html>

61
website/help_log.html Normal file
View File

@@ -0,0 +1,61 @@
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<title>ViewCVS Help: Classic CVS Log View</title>
</head>
<body background="images/chalk.jpg">
<table width="100&#37;" cellspacing=5>
<tr>
<td width="1%"><a href="http://viewcvs.sf.net/index.html"><img border=0
src="images/logo.png"></a>
</td>
<td>
<h1>ViewCVS Help: Classic CVS Log View</h1>
</td>
</tr>
<tr><td width="1%" valign=top bgcolor="#ffffff">
<h3>Help</h3>
<a href="help_rootview.html">General</a><br>
<a href="help_dirview.html">Directory&nbsp;View</a><br>
<b>Classic&nbsp;Log&nbsp;View</b><br>
<a href="help_logtable.html">Alternative&nbsp;Log&nbsp;View</a><br>
<a href="help_query.html">Query&nbsp;Database</a><br>
<h3>Internet</h3>
<a href="http://viewcvs.sf.net/index.html">Home</a><br>
<a href="http://viewcvs.sf.net/upgrading.html">Upgrading</a><br>
<a href="http://viewcvs.sf.net/contributing.html">Contributing</a><br>
<a href="http://viewcvs.sf.net/license-1.html">License</a><br>
</td><td colspan=2>
<p>
The log view displays the revision history of the selected source
file. For each revision the following information is displayed:
<ul>
<li>the revision number (clickable to download it)</li>
<li>a link to view this revision annotated</li>
<li>a link to select this revision for diffs (see below)</li>
<li>the date and age of this change</li>
<li>and the author of this modification.</li>
<li>The CVS branch (usually <code>MAIN</code>, if not on a branch).</li>
<li>Possibly a list of CVS tags bound to this revision (if any).</li>
<li>The size of this change measured in added and removed lines of
code.</li>
<li>Links to view Diffs to the previous revision or possibly to
an arbitrary selected revision (if any, see above).
<li>And last but not least, the commit log message which should tell
about the reason for this change.</li>
</ul>
</p><p>
At the bottom of such a page you will find a form which allows
to request diffs between arbitrary revisions.
</p>
</td></tr></table>
<hr>
<address><a href="mailto:viewcvs-dev@lyra.org">ViewCVS Group</a></address>
<!-- Created: Thu Oct 25 22:08:29 CEST 2001 -->
<!-- hhmts start -->
Last modified: Wed Dec 12 13:56:52 CET 2001
<!-- hhmts end -->
</body>
</html>

View File

@@ -0,0 +1,59 @@
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<title>ViewCVS Help: CVS Log Table View</title>
</head>
<body background="images/chalk.jpg">
<table width="100&#37;" cellspacing=5>
<tr>
<td width="1%"><a href="http://viewcvs.sf.net/index.html"><img border=0
src="images/logo.png"></a>
</td>
<td>
<h1>ViewCVS Help: CVS Log Table View</h1>
</td>
</tr>
<tr><td width="1%" valign=top bgcolor="#ffffff">
<h3>Help</h3>
<a href="help_rootview.html">General</a><br>
<a href="help_dirview.html">Directory&nbsp;View</a><br>
<a href="help_log.html">Classic&nbsp;Log&nbsp;View</a><br>
<b>Alternative&nbsp;Log&nbsp;View</b><br>
<a href="help_query.html">Query&nbsp;Database</a><br>
<h3>Internet</h3>
<a href="http://viewcvs.sf.net/index.html">Home</a><br>
<a href="http://viewcvs.sf.net/upgrading.html">Upgrading</a><br>
<a href="http://viewcvs.sf.net/contributing.html">Contributing</a><br>
<a href="http://viewcvs.sf.net/license-1.html">License</a><br>
</td><td colspan=2>
<p>
The table based log view must be enabled in the site wide
ViewCVS configuration file.
</p><p>
It displays the revision history of the selected source
file in a table containing the following columns:
<ul>
<li><b>Revision</b>: The CVS revision number.</li>
<li><b>Tasks</b>: This column contains several links to view
a revision.</li>
<li><b>Diffs</b>: Several links to view the changes between this
and other revisions.
<li><b>Branches and Tags</b>: Displays the branch and a pulldown
menu button displaying all tags bound to that revision</li>
<li>FIXME: Document the other columns</li>
</ul>
</p><p>
At the bottom of such a page you will find a form which allows
to request diffs between arbitrary revisions.
</p>
</td></tr></table>
<hr>
<address><a href="mailto:viewcvs-dev@lyra.org">ViewCVS Group</a></address>
<!-- Created: Thu Oct 25 22:08:29 CEST 2001 -->
<!-- hhmts start -->
Last modified: Sat Dec 8 23:26:52 CET 2001
<!-- hhmts end -->
</body>
</html>

64
website/help_query.html Normal file
View File

@@ -0,0 +1,64 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"
"http://www.w3.org/TR/REC-html40/loose.dtd">
<html><head>
<title>ViewCVS Help: Query The Commit Database</title>
</head>
<body background="images/chalk.jpg">
<table width="100%" border=0 cellspacing=5 cellpadding=0>
<tr>
<td width="1%"><a href=".."><img border=0
src="images/logo.png"></a>
</td>
<td><h1>ViewCVS Help: Query The Commit Database</h1></td>
</tr>
<tr><td width="1%" valign="top" bgcolor="#ffffff">
<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>
<b>Query&nbsp;Database</b>
<h3>Internet</h3>
<a href="http://viewcvs.sf.net/index.html">Home</a><br>
<a href="http://viewcvs.sf.net/upgrading.html">Upgrading</a><br>
<a href="http://viewcvs.sf.net/contributing.html">Contributing</a><br>
<a href="http://viewcvs.sf.net/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 <i>jpaint</i> and <i>gstein</i>, just type: <code>jpaint,
gstein</code> in the <i>Author</i> 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 <i>.py</i> extention is:
<code>l"%.py"</code> in the <i>File</i> 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,83 @@
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<title>ViewCVS Help: Toplevel Directory View</title>
</head>
<body background="images/chalk.jpg">
<table width="100&#37;" cellspacing=5>
<tr>
<td width="1%"><a href=".."><img border=0
src="images/logo.png"></a>
</td>
<td>
<h1>ViewCVS Help: Toplevel Directory View</h1>
</td>
</tr>
<tr><td width="1%" valign=top bgcolor="#ffffff">
<h3>Help</h3>
<b>General</b>
<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>
<a href="help_query.html">Query&nbsp;Database</a><br>
<h3>Internet</h3>
<a href="http://viewcvs.sf.net/index.html">Home</a><br>
<a href="http://viewcvs.sf.net/upgrading.html">Upgrading</a><br>
<a href="http://viewcvs.sf.net/contributing.html">Contributing</a><br>
<a href="http://viewcvs.sf.net/license-1.html">License</a><br>
</td><td colspan=2>
<p><b>ViewCVS</b> is a WWW interface for CVS Repositories.</b>
You can browse the
file hierarchy by picking directories (which have slashes after
them, <i>e.g.</i>, <b>src/</b>). If you pick a file, you will see
the revision history for that file. Selecting a revision number
will display that revision of the file. There is a link at each
revision to display diffs between that revision and the previous
one, and a form at the bottom of the page that allows you to
display diffs between arbitrary revisions.
</p>
<h3>ViewCVS History and Credits</h3>
<p>
This program
(<a href="http://viewcvs.sourceforge.net/">ViewCVS</a>)
has been written by Greg Stein
&lt;<a href="mailto:gstein@lyra.org">gstein@lyra.org</a>&gt;
based on the
<a href="http://linux.fh-heilbronn.de/~zeller/cgi/cvsweb.cgi">cvsweb</a>
script by Henner Zeller
&lt;<a href="mailto:zeller@think.de">zeller@think.de</a>&gt;;
it is covered by the
<a href="http://www.opensource.org/licenses/bsd-license.html">BSD-License</a>.
If you would like to use this CGI script on your own web server and
CVS tree, see Greg's
<a href="http://viewcvs.sourceforge.net/">ViewCVS&nbsp;distribution&nbsp;site</a>.
Please send any suggestions, comments, etc. to the
<a href="mailto:viewcvs-dev@lyra.org">ViewCVS&nbsp;Developers&nbsp;Mailinglist</a>.
</p>
<h3>Documentation about CVS</h3>
<blockquote>
<p>
<a href="http://cvsbook.red-bean.com/">Karl Fogel's CVS book</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>
<!-- insert repository access instructions here -->
</td></tr></table>
<hr>
<address><a href="mailto:viewcvs-dev@lyra.org">ViewCVS Group</a></address>
<!-- Created: Thu Oct 25 22:16:29 CEST 2001 -->
<!-- hhmts start -->
Last modified: Tue Nov 13 20:41:02 CEST 2001
<!-- hhmts end -->
</body>
</html>

BIN
website/images/chalk.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 755 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 B

BIN
website/images/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

View File

@@ -3,144 +3,290 @@
<head>
<title>ViewCVS: Viewing CVS Repositories</title>
</head>
<!-- Editors: Please keep all links to external sites in italics. -->
<body background="images/chalk.jpg">
<table width="100&#37;" cellspacing=5>
<tr>
<td width="1%"><img border=0 src="images/logo.png"></td>
<td>
<h1>ViewCVS: Viewing CVS Repositories</h1>
</td>
<td align=center valign=top bgcolor="white" width="1%">
<b>Quickstart:</b>
<a href="viewcvs-0.9.4.tar.gz">download</a>
</td>
<td width="1%"><a href="http://sourceforge.net/"><img border=0
src="http://sourceforge.net/sflogo.php?group_id=18760&type=1"></a><br><a href="http://sourceforge.net/projects/viewcvs/">ViewCVS&nbsp;project&nbsp;page</a>
</td>
</tr>
<tr><td width="1%" valign=top>
<h3>Links:</h3>
<a href="http://viewcvs.sourceforge.net">Home</a><br>
<a href="upgrading.html">Upgrading</a><br>
<a href="contributing.html">Contributing</a><br>
<a href="license-1.html">License</a><br>
<a href="who.html">Who</a><br>
<h3>Sections:</h3>
<a href="#Features">Features</a><br>
<a href="#History">History</a><br>
<a href="#Mail">Mailing Lists</a><br>
<a href="#Cvsweb">vs.&nbsp;cvsweb</a><br>
<a href="#Download">Download</a><br>
<a href="#Future">Future&nbsp;directions</a><br>
<a href="#Colorize">Colorization</a><br>
</td>
<td colspan=3>
<body background="/images/chalk.jpg">
<h1>ViewCVS: Viewing CVS Repositories</h1>
<p>
The ViewCVS software was inspired by
<a href="http://linux.fh-heilbronn.de/~zeller/cgi/cvsweb.cgi"><i>cvsweb</i></a>
(by <a href="mailto:zeller@think.de">Henner Zeller</a>).
I wanted to make some changes and updates, but cvsweb was
implemented in Perl (and rather poorly, IMO). So I undertook the
task to convert the software to
<a href="http://www.python.org/"><i>Python</i></a>.
</p>
<hr width="75&#37;">
<h2><a name="Features">Features</a></h2>
<p>
ViewCVS can browse directories, change logs, and specific
revisions of files. It can display diffs between versions and
show selections of files based on tags or branches.
show selections of files based on tags or branches. In addition,
ViewCVS has "annotation" or "blame" support, Bonsai-like query
facilities, template-based page generation, and support for
individually configuring virtual hosts.
It also includes support for
<a href="http://www.akhphd.au.dk/~bertho/cvsgraph/"><i>CvsGraph</i></a>
-- a program to display the tree of revisions and branches
graphically.
</p>
<p>
ViewCVS is currently at version 0.3. It is almost a straight
port of the cvsweb script, but has had numerous cleanups and other
modifications, based on some of Python's strengths. There is
still some "badness" in there, but I've been working on flushing
that out, along with a few new features. The
functionality of ViewCVS is equal to that of cvsweb, minus the
"annotation" support. Annotation requires read/write access to
the CVS repository (at this time), and I believe that is a
Bad Thing to do. One of my tasks will be eliminating the
read/write requirement.
Currently, the functionality of ViewCVS surpasses that of cvsweb.
See <a href="#Cvsweb">below</a> for a list of additional features.
</p>
<hr width="75&#37;">
<h2><a name="History">History</a></h2>
<p>
The ViewCVS software was inspired by
<a href="http://stud.fh-heilbronn.de/~zeller/cgi/cvsweb.cgi/"><i>cvsweb</i></a>
(originally written by Bill Fenner and then further developed by
<a href="mailto:zeller@think.de">Henner Zeller</a>).
Greg Stein wanted to make some changes and updates, but cvsweb was
implemented in Perl. He wrote:
<blockquote><i>While I can manage some Perl, cvsweb was
rather unmaintainable for me. So I undertook the
task to convert the software to
<a href="http://www.python.org/">Python</a>. As a result,
I've actually been able to go <em>way</em> beyond the simple
changes that I had envisioned.
</i></blockquote>
</p>
<p>
ViewCVS started as a port of the cvsweb script, but has had
numerous cleanups and other modifications, based on some of
Python's strengths. There is still some minor "badness"
remaining from the Perl code, but Greg has been working on
flushing that out, while adding new features.
</p>
<p>
ViewCVS has been developed by the <a href="who.html">ViewCVS
Group</a> and is made available under a
<a href="license-1.html">BSD-type license</a>.
</p>
<p>
ViewCVS requires <strong>Python 1.5</strong> or later (Python
1.5 has been out for a couple years and is readily available for
your favorite operating system). If you choose to use the SQL Checkin
Database feature, then you must use <strong>Python 1.5.2</strong>
or later and have the
<a href="http://sourceforge.net/projects/mysql-python"><i>MySQLdb
module</i></a> installed which itself requires
Marc-Andre Lemburgs
<a href="http://www.lemburg.com/files/python/eGenix-mx-Extensions.html#Download-mxBASE"><i>mxDateTime extension</i></a>.
</p>
<hr width="75&#37;">
<h2><a name="Mail">Mailing Lists</a></h2>
<p>
If you have any comments, questions, or suggestions,
then please send them to the
<a href="http://mailman.lyra.org/mailman/listinfo/viewcvs"><i>ViewCVS
mailing list</i></a>, which is also
<a href="http://mailman.lyra.org/pipermail/viewcvs/"><i>archived</i></a>.
</p>
<p>
A <a href="http://mailman.lyra.org/mailman/listinfo/viewcvs-dev"><i>mailing
list for ViewCVS developers</i></a> is also available
(<a href="http://mailman.lyra.org/pipermail/viewcvs-dev/"><i>Archive</i></a>).
</p>
<p>
ViewCVS is an <a href="http://www.opensource.org/"><i>Open
Source</i></a> project, and all
<a href="contributing.html">contributions</a> are welcome.
</p>
<hr width="75&#37;">
<h2><a name="Cvsweb">Additional features over cvsweb</a></h2>
<ul>
<li>
Template support: you can now customize the look and feel of
ViewCVS by editing the provided EZT templates, which are used
to generate the pages.
</li>
<li>
Internationalization support: ViewCVS will parse and handle
the Accept-Language request header, and can select different
inputs for localized page generation.
</li>
<li>
Colorization for many file types via <code>enscript</code>.
</li>
<li>
Bonsai-like query features. (Requires MySQL and some other
prerequisites)
</li>
<li>
Annotation/blame viewing support against a <strong>read-only</strong>
repository.
</li>
<li>
Configuration on a per-virtual-host basis. This allows you
to share the configuration file and ViewCVS installation
across virtual hosts, yet still be able to fine-tune the
options when necessary.
</li>
<li>
Automatic generation of tarballs for the HEAD or a specified
tag.
</li>
<li>
Runs either as CGI script, called from an installed web server
(such as <a href="http://httpd.apache.org/"><i>Apache</i></a>),
or as a standalone server.
</li>
<li>
Searching files for matches for a regular expression.
</li>
<li>Better reporting for unreadable files.</li>
<li>
More robust when given varying <code>rcsdiff</code> or
<code>rlog</code> outputs.
</li>
<li>Hard breaks in human-readable diffs.</li>
<li>
The configuration file is optional (you can change the values
right in the CGI script and avoid the config file, if you so
choose). The config file syntax is also cleaner, since it is
human-manageable rather than source code.
</li>
<li>
Directories with a large number of files can be viewed.
</li>
<li>
<strong>Security</strong>: ViewCVS only requires read access
to the CVS repository (not read/write). With the correct
security partitioning, this means that even if ViewCVS were to
be subverted, your source code is safe. Further, ViewCVS does
not use any <code>system()</code> or <code>popen()</code>
calls, which are very susceptible to abuse.
<br>
<small>(cvsweb had a hole due to a popen() call)</small>
</li>
<li>
Last but not least: it doesn't suffer from the "unmaintainable
code effect" that hits most Perl projects sooner or later:
<blockquote><i>[Perl] combines all the worst aspects of C and Lisp:
a billion different sublanguages in one monolithic executable.
It combines the power of C with the readability of PostScript.</i>
--&nbsp;Jamie&nbsp;Zawinski
</blockquote>
</li>
</ul>
<p>
The changes present in each release are available in
<a href="http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/viewcvs/viewcvs/CHANGES?rev=HEAD">ViewCVS's
CHANGES file</a>.
</p>
<hr width="75&#37;">
<h2><a name="Download">Download</a></h2>
<p>
The software is available for download:
</p>
<blockquote>
<a href="viewcvs-0.3.tar.gz">Version 0.3 of ViewCVS</a>
<a href="viewcvs-0.9.4.tar.gz">Version 0.9.4 of ViewCVS as a gzipped
tar</a>
<br>
<a href="viewcvs-0.9.4.zip">Version 0.9.4 of ViewCVS as a ZIP
file</a>
</blockquote>
<p>
Of course, it is also available through ViewCVS itself:
Of course the current development version is also available
through ViewCVS itself:
</p>
<blockquote>
<a href="/cgi-bin/viewcvs.cgi/gjspy/viewcvs/">http://www.lyra.org/cgi-bin/viewcvs.cgi/gjspy/viewcvs/</a>
<a href="http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/viewcvs/">http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/viewcvs/</a>
</blockquote>
<p>
ViewCVS <font color=red>requires <strong>Python
1.5.2</strong></font>.
</p>
<p>
[ <a href="../">up to Python software</a> ]
[ <a href="../../">up to Greg's page</a> ]
You can also
<a href="http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/viewcvs/viewcvs/CHANGES?rev=HEAD">see
the changes</a> for this release.
</p>
<hr width="75&#37;">
<h2>Mailing List</h2>
<h2><a name="Future">Future features, directions</a></h2>
<p>
If you have any comments, questions, suggestions, or patches,
then please send them to the
<a href="http://mailman.lyra.org/mailman/listinfo/viewcvs">ViewCVS
mailing list</a>.
</p>
<hr width="75&#37;">
<h2>FAQ</h2>
<p>Kinda short :-)</p>
<dl>
<!-- one of (dd dt) -->
<dt><strong>I got an <code>AttributeError: urlencode</code></strong></dt>
<dd>
If you see the following traceback in your browser:
<blockquote>
<pre>
Traceback (innermost last):
File "/home/httpd/cgi-bin/viewcvs.cgi", line 2205, in ?
main()
File "/home/httpd/cgi-bin/viewcvs.cgi", line 2123, in main
query_string = sticky_query(query_dict)
File "/home/httpd/cgi-bin/viewcvs.cgi", line 444, in sticky_query
bare_query = urllib.urlencode(sticky_dict)
AttributeError: urlencode
</pre>
</blockquote>
This is indicative of using an earlier version of
Python. <code>urllib.urlencode()</code> was introduced in
Python 1.5.2.
</dd>
</dl>
<hr width="75&#37;">
<h2>Additional features over cvsweb</h2>
<ul>
<li>Colorization for Python files</li>
<li>Better reporting for unreadable files</li>
<li>More robust when given varying <code>rcsdiff</code> or
<code>rlog</code> outputs</li>
<li>Hard breaks in human-readable diffs</li>
</ul>
<p>
Future features, coming soon:
ViewCVS is a Open Source project. So any future development depends
on the contributions that will be made by its user community.
Certainly working patches have a greater chance to become realized
quickly than feature requests. <i>But don't hesitate to submit your
suggestions! Send mail to the
<a href="mailto:viewcvs@lyra.org">viewcvs@lyra.org</a>
mailing list or even better use the
<a href="http://sourceforge.net/tracker/?func=add&group_id=18760&atid=368760">SF&nbsp;tracker</a>.
</i>
</p>
<ul>
<li>
Optional, separate configuration file (similar to
cvsweb). This will make upgrading easier.
<li>See the feature requests already submitted through
the <a href="http://sourceforge.net/tracker/?atid=368760&group_id=18760&func=browse">SF feature request tracker</a>
</li>
<li>UI streamlining/simplification</li>
<li>Integration with CVS checkin auto-mail scripts</li>
<li>
<i>Suggestions? Send mail to the
<a href="mailto:viewcvs@lyra.org">viewcvs@lyra.org</a>
mailing list.
</i>
</li>
<li>Tighter integration with the query features</li>
<p>
</ul>
<p>
Longer term:
And another longer term pet of Greg Stein:
</p>
<ul>
<li>Annotation ("blame") support (similar to cvsweb)</li>
<li>Integration with an indexer such as LXR</li>
<li>Additional colorizers</li>
</ul>
</p>
<hr width="75&#37;">
<h2>Colorization of Python files</h2>
<h2><a name="Colorize">Colorization of files</a></h2>
<p>
ViewCVS can make use of the <code>enscript</code> program to
colorize files in the CVS repository. If <code>enscript</code>
is present on your system, then set the
<code>use_enscript</code> option in the
<code>viewcvs.conf</code> configuration file to <code>1</code>.
If necessary,
update the <code>enscript_path</code> option to point to your
installation directory. ... That's it! Now, as you view files
through ViewCVS, they will be colored.
</p>
<h3>Colorization of Python files</h3>
<p>
ViewCVS currently also comes with a builtin colorizer for Python
source files. This may go away in a future version, given the new
<code>enscript</code> support...
</p>
<p>
Christophe Pelte suggested this feature: colorize Python source
files using
<a href="http://starship.python.net/crew/lemburg/SoftwareDescriptions.html#py2html.py">py2html</a>
<a href="http://starship.python.net/crew/lemburg/SoftwareDescriptions.html#py2html.py"><i>py2html</i></a>
(by Marc-Andrew Lemburg, based on
<a href="http://starship.python.net/crew/just/code/PyFontify.py">PyFontify</a>
<a href="http://starship.python.net/crew/just/code/PyFontify.py"><i>PyFontify</i></a>
by Just van Rossum). I've added this feature to ViewCVS 0.3,
along with a generalized plugin mechanism for custom coloring other
types of files. See the instructions within the viewcvs.cgi for
@@ -148,11 +294,12 @@ AttributeError: urlencode
you want to use this feature.
</p>
<hr>
<address><a href="mailto:gstein@lyra.org">Greg Stein</a></address>
</td></tr></table>
<hr>
<address><a href="mailto:viewcvs@lyra.org">ViewCVS Users Group</a></address>
<!-- Created: Fri Dec 3 02:51:37 PST 1999 -->
<!-- hhmts start -->
Last modified: Fri Feb 11 07:51:53 PST 2000
Last modified: Tue Jan 15 01:51:03 PST 2002
<!-- hhmts end -->
</body>
</html>

102
website/license-1.html Normal file
View File

@@ -0,0 +1,102 @@
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<title>ViewCVS License Agreement (v1)</title>
</head>
<body background="images/chalk.jpg">
<table width="100&#37;" cellspacing=5>
<tr>
<td width="1%"><a href="index.html"><img border=0
src="images/logo.png"></a>
</td>
<td>
<h1>ViewCVS License Agreement (v1)</h1>
</td>
<td width="1%"><a href="http://sourceforge.net/"><img border=0
src="http://sourceforge.net/sflogo.php?group_id=18760&type=1"></a><br><a href="http://sourceforge.net/projects/viewcvs/">ViewCVS&nbsp;project&nbsp;page</a>
</td>
</tr>
<tr><td width="1%" valign=top>
<a href="index.html">Overview</a><br>
<a href="upgrading.html">Upgrading</a><br>
<a href="contributing.html">Contributing</a><br>
License<br>
<a href="who.html">Who</a><br>
</td><td colspan=2>
<p>
The following text constitutes the license agreement for the
<a href="./">ViewCVS</a> software. It
is an agreement between
<a href="who.html">The ViewCVS
Group</a> and the users of ViewCVS.
</p>
<p>
<small>
<em>
Note: the copyright years were updated on May 12, 2001. No
other changes were made to the license.
</em>
</small>
</p>
<hr>
<p>
<b>
Copyright &copy; 1999-2001 The ViewCVS Group. All rights reserved.
</b>
</p>
<p>
By using ViewCVS, you agree to the terms and conditions set
forth below:
</p>
<p>
Redistribution and use in source and binary forms, with or
without modification, are permitted provided that the following
conditions are met:
</p>
<ol>
<li>
<p>
Redistributions of source code must retain the above
copyright notice, this list of conditions and the following
disclaimer.
</p>
</li>
<li>
<p>
Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials
provided with the distribution.
</p>
</li>
</ol>
<p>
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS
IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
</p>
</td></tr></table>
<hr>
<address><a href="mailto:viewcvs@lyra.org">ViewCVS Users Group</a></address>
<!-- Created: Mon May 8 19:01:27 PDT 2000 -->
<!-- hhmts start -->
Last modified: Sat Oct 20 16:09:37 PDT 2001
<!-- hhmts end -->
</body>
</html>

356
website/upgrading.html Normal file
View File

@@ -0,0 +1,356 @@
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<title>Upgrading a ViewCVS Installation</title>
</head>
<body background="images/chalk.jpg">
<table width="100&#37;" cellspacing=5>
<tr>
<td width="1%"><a href="index.html"><img border=0
src="images/logo.png"></a>
</td>
<td>
<h1>Upgrading a ViewCVS Installation</h1>
</td>
<td width="1%"><a href="http://sourceforge.net/"><img border=0
src="http://sourceforge.net/sflogo.php?group_id=18760&type=1"></a><br><a href="http://sourceforge.net/projects/viewcvs/">ViewCVS&nbsp;project&nbsp;page</a>
</td>
</tr>
<tr><td width="1%" valign=top>
<a href="index.html">Overview</a><br>
Upgrading<br>
<a href="contributing.html">Contributing</a><br>
<a href="license-1.html">License</a><br>
<a href="who.html">Who</a><br>
</td><td colspan=2>
<p>
This document describes some of the things that you will need to
consider, change, or handle when upgrading an existing ViewCVS
installation to a newer version.
</p>
<p>
It is always recommended to install the new version in a fresh directory
and to carefully compare the configuration files. A possible approach
is to name the directories <code>/usr/local/viewcvs-0.6</code>,
<code>/usr/local/viewcvs-0.7</code> and so on and than create a
symbolic link <code>viewcvs</code> pointing to the production
version. This way you can easily test several versions and switch
back, if your users start to complain.
</p>
<ul>
<li><a href="#from8">Upgrading from ViewCVS 0.8</a></li>
<li><a href="#from7">Upgrading from ViewCVS 0.7 or earlier</a></li>
</ul>
<hr>
<h2><a name="from8">Upgrading from ViewCVS 0.8</a></h2>
<p>
This section discusses how to upgrade ViewCVS 0.8 to version
0.9 or a later version of the software.
</p>
<h3>Configuration Options</h3>
<p>
More templates were introduced in version 0.8 of the software,
which made many of the configuration options obsolete. This
section covers which options were removed. If you made any
changes to these options, then you will need to make
corresponding changes in the templates.
</p>
<blockquote>
<dl>
<dt>
Colors:
<strong>diff_heading</strong>,
<strong>diff_empty</strong>,
<strong>diff_remove</strong>,
<strong>diff_change</strong>,
<strong>diff_add</strong>,
and <strong>diff_dark_change</strong>
</dt>
<dd>
These options have been incorporated into the
<code>diff.ezt</code> template.
<p></p>
</dd>
<dt><strong>markup_log</strong></dt>
<dd>
This option has been incorporated into the
<code>markup.ezt</code> template.
<p></p>
</dd>
<dt>Colors: <strong>nav_header</strong>
and <strong>alt_background</strong></dt>
<dd>
These options have been incorporated into the
<code>header.ezt</code> template.
<p></p>
</dd>
<dt>
Images:
<strong>back_icon</strong>,
<strong>dir_icon</strong>,
and <strong>file_icon</strong>
</dt>
<dd>
These options have been incorporated into the
<code>directory.ezt</code>, <code>header.ezt</code>,
<code>log.ezt</code>, <code>log_table.ezt</code>, and
<code>query.ezt</code> templates.
<p></p>
</dd>
<dt><strong>use_java_script</strong>
and <strong>open_extern_window</strong></dt>
<dd>
The templates now use JavaScript in all applicable places,
and open external windows for most downloading and viewing
of files. If you wish to not use JavaScript and/or external
windows, then remove the feature(s) from the templates.
<p></p>
</dd>
<dt><strong>show_author</strong></dt>
<dd>
Changing this option would be quite strange and rare. If you
do not want to show the author for the revisions, then you
should remove it from the various templates.
<p></p>
</dd>
<dt><strong>hide_non_readable</strong></dt>
<dd>
This option was never used, so it has been removed.
<p></p>
</dd>
<dt><strong>flip_links_in_dirview</strong></dt>
<dd>
This option is no longer available. If you want the links in
your directory view flipped, then you may use the
<code>dir_alternate.ezt</code> template.
<p></p>
</dd>
</dl>
</blockquote>
<h3>Template Variables</h3>
<p>
Some template variables that were available in 0.8 have been
removed in 0.9. If you have custom templates that refer to these
variables, then you will need to modify your templates.
</p>
<blockquote>
<dl>
<dt><code>directory.ezt</code>: <var>headers</var></dt>
<dd>
The headers are now listed explicitly in the template,
rather than made available through a list.
<p></p>
</dd>
<dt>
<code>directory.ezt</code>:
<var>rows.cols</var>,
and <var>rows.span</var>
</dt>
<dd>
These variables were used in conjunction with the
<var>headers</var> variable to control the column
displays. This is now controlled explicitly within the
templates.
<p></p>
</dd>
<dt><code>directory.ezt</code>:
<var>rev_in_front</var></dt>
<dd>
This was used to indicate that revision links should
be used in the first column, rather than in their
standard place in the second column. Changing the
links should now be done in the template, rather than
according to this variable. You may want to look at
the <code>dir_alternate.ezt</code> template, which has
the revision in front.
<p></p>
</dd>
<dt><code>directory.ezt</code>:
<var>rows.attic</var>
and <var>rows.hide_attic_href</var></dt>
<dd>
These variable were used to manage the hide and
showing of the contents of the <code>Attic/</code>
subdirectory. Several new variables were introduced
which can be used to replace this functionality:
<var>show_attic_href</var>,
<var>hide_attic_href</var>, and <var>rows.state</var>.
<p></p>
</dd>
</dl>
</blockquote>
<hr>
<h2><a name="from7">Upgrading from ViewCVS 0.7 or earlier</a></h2>
<p>
This section discusses how to upgrade ViewCVS 0.7, or earlier,
to 0.8 or a later version of the software.
</p>
<p>
<strong>NOTE:</strong> these changes will bring you up to the
requirements of version 0.8. You must also follow the directions
for <a href="#from8">upgrading from 0.8</a>.
</p>
<h3>Configuration Options</h3>
<p>
The largest change from 0.7 to 0.8, that you will need to deal
with, is the introduction of templates. This shifted many
configuration file options into the templates, for more direct
editing of the output style, colors, and layout. Below is a list
of options that no longer exist, and where you can find their
counterpart in the current version of ViewCVS.
</p>
<p>
The following options have all been removed in ViewCVS 0.8. If
you made local changes to your ViewCVS configuration, then you
will need to edit templates in the <code>templates/</code>
subdirectory.
</p>
<blockquote>
<dl>
<dt>
The [text] section:
<strong>short_intro</strong>,
<strong>long_intro</strong>,
and <strong>doc_info</strong>
</dt>
<dd>
These options have been incorporated into the
<code>doc/help_rootview.html</code> page and the
<code>doc/help_dirview.html</code> page.
<p></p>
</dd>
<dt><strong>repository_info</strong></dt>
<dd>
This option is now incorporated into the
<code>directory.ezt</code> template.
<p></p>
</dd>
<dt><strong>table_padding</strong></dt>
<dd>
The table padding values can be changed in the
<code>directory.ezt</code> template.
<p></p>
</dd>
<dt><strong>table_border</strong></dt>
<dd>
Edit <code>directory.ezt</code> to add a border around the
directory table.
<p></p>
</dd>
<dt>
<strong>column_header_normal</strong> and
<strong>column_header_sorted</strong>
</dt>
<dd>
Edit <code>directory.ezt</code> to modify the colors of the
column headers.
<p></p>
</dd>
<dt>
<strong>extern_window_width</strong> and
<strong>extern_window_height</strong>
</dt>
<dd>
These options were never used and have been removed.
<p></p>
</dd>
<dt><strong>logo</strong></dt>
<dd>
Edit the templates directly (<code>directory.ezt</code>,
<code>log.ezt</code> or <code>log_table.ezt</code> and if
needed <code>query.ezt</code>) to alter the URL and size of
your logo.
<p></p>
</dd>
<dt><strong>allow_version_select</strong></dt>
<dd>
Edit the <code>log.ezt</code> template if you want to remove
the link which allows the user to select a revision for a
diff.
<p></p>
</dd>
<dt><strong>input_text_size</strong></dt>
<dd>
Edit the <code>log.ezt</code> template if you want to change
the size of the entry box for revisions for performing
diffs.
<p></p>
</dd>
<dt><strong>even_odd</strong></dt>
<dd>
Edit the <code>directory.ezt</code> and
<code>query.ezt</code> templates if you want to change the
colors of the rows in the directory and query result tables.
<p></p>
</dd>
<dt>Colors: <strong>text</strong>
and <strong>background</strong></dt>
<dd>
These options have been incorporated into the
<code>directory.ezt</code>, <code>log.ezt</code>, and
<code>log_table.ezt</code> templates.
<p></p>
</dd>
</dl>
</blockquote>
</td></tr></table>
<hr>
<address><a href="mailto:viewcvs@lyra.org">ViewCVS Users Group</a></address>
<!-- Created: Mon Sep 24 04:23:53 PDT 2001 -->
<!-- hhmts start -->
Last modified: Sat Dec 22 20:05:14 PST 2001
<!-- hhmts end -->
</body>
</html>

59
website/who.html Normal file
View File

@@ -0,0 +1,59 @@
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<title>The ViewCVS Group</title>
</head>
<body background="images/chalk.jpg">
<table width="100&#37;" cellspacing=5>
<tr>
<td width="1%"><a href="index.html"><img border=0
src="images/logo.png"></a>
</td>
<td>
<h1>The ViewCVS Group</h1>
</td>
<td width="1%"><a href="http://sourceforge.net/"><img border=0
src="http://sourceforge.net/sflogo.php?group_id=18760&type=1"></a><br><a href="http://sourceforge.net/projects/viewcvs/">ViewCVS&nbsp;project&nbsp;page</a>
</td>
</tr>
<tr><td width="1%" valign=top>
<a href="index.html">Overview</a><br>
<a href="upgrading.html">Upgrading</a><br>
<a href="contributing.html">Contributing</a><br>
<a href="license-1.html">License</a><br>
Who<br>
</td><td colspan=2>
<p>
The ViewCVS Group is an informal group of people working on and
developing the ViewCVS package. The current set of members are:
</p>
<ul>
<li><a href="http://www.lyra.org/greg/"><b>Greg Stein</b></a></li>
<li>Tanaka Akira</li>
<li>Tim Cera</li>
<li>Peter Funk</li>
<li>Jay Painter</li>
</ul>
<p>
In 2001 the project has been moved to SourceForge and some
<a href="http://sourceforge.net/project/memberlist.php?group_id=18760">more
developers</a> were given commit access.
</p>
<p>
Please note that the <a href="./">ViewCVS</a> package is offered
under a BSD-type license, which is detailed on the
<a href="license-1.html">ViewCVS License</a> page.
</p>
</td></tr></table>
<hr>
<address><a href="mailto:viewcvs-dev@lyra.org">ViewCVS Group</a></address>
<!-- Created: Mon May 8 19:08:58 PDT 2000 -->
<!-- hhmts start -->
Last modified: Thu Oct 25 01:47:02 PDT 2001
<!-- hhmts end -->
</body>
</html>