Bug 176316 Add Last-Modified support to bonsai
r=tara git-svn-id: svn://10.0.0.236/trunk@138955 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
@@ -22,7 +22,6 @@
|
||||
#
|
||||
# Contributor(s):
|
||||
|
||||
|
||||
# Created: Steve Lamm <slamm@netscape.com>, 12-Sep-97.
|
||||
# Modified: Marc Byrd <byrd@netscape.com> , 19971030.
|
||||
#
|
||||
@@ -66,8 +65,6 @@ if ($ENV{REQUEST_METHOD} eq 'POST' and defined $::FORM{set_line}) {
|
||||
print "Set-Cookie: line_nums=$::FORM{set_line}; expires="
|
||||
. toGMTString(time + 86400 * 152) . "; path=/\n";
|
||||
}
|
||||
print "\n";
|
||||
|
||||
|
||||
# Some Globals
|
||||
#
|
||||
@@ -96,6 +93,7 @@ my $filename = '';
|
||||
$filename = $::FORM{file} if defined $::FORM{file};
|
||||
if ($filename eq '')
|
||||
{
|
||||
print "\n";
|
||||
&print_usage;
|
||||
exit;
|
||||
}
|
||||
@@ -120,6 +118,7 @@ if (defined $root and $root ne '') {
|
||||
if (-d $root) {
|
||||
unshift(@src_roots, $root);
|
||||
} else {
|
||||
print "\n";
|
||||
&print_top;
|
||||
print "Error: Root, $root, is not a directory.<BR><BR>\n";
|
||||
print "</BODY></HTML>\n";
|
||||
@@ -143,6 +142,7 @@ foreach (@src_roots) {
|
||||
}
|
||||
|
||||
unless ($found_rcs_file) {
|
||||
print "\n";
|
||||
&print_top;
|
||||
my $escaped_filename = html_quote($filename);
|
||||
print "Rcs file, $escaped_filename, does not exist.<pre>rcs_filename => '$rcs_filename'\nroot => '$root'</pre><BR><BR>\n";
|
||||
@@ -156,18 +156,27 @@ my $rcs_path;
|
||||
|
||||
CheckHidden($rcs_filename);
|
||||
|
||||
|
||||
# Parse the rcs file ($::opt_rev is passed as a global)
|
||||
#
|
||||
$revision = &parse_cvs_file($rcs_filename);
|
||||
my $file_rev = $revision;
|
||||
|
||||
my @text = &extract_revision($revision);
|
||||
die "$::progname: Internal consistency error" if $#text != $#::revision_map;
|
||||
|
||||
if ($#text != $#::revision_map) {
|
||||
print "\n";
|
||||
die "$::progname: Internal consistency error"
|
||||
}
|
||||
|
||||
# Raw data opt (so other scripts can parse and play with the data)
|
||||
&print_raw_data, exit if defined $::FORM{data};
|
||||
if (defined $::FORM{data}) {
|
||||
print "\n";
|
||||
&print_raw_data;
|
||||
exit;
|
||||
}
|
||||
|
||||
print "Last-Modified: ".time2str("%a, %d %b %Y %T %Z", str2time($::revision_ctime{$::opt_rev}), "GMT")."\n";
|
||||
print "\n";
|
||||
#ENDHEADERS!!
|
||||
|
||||
# Handle the "line_nums" argument
|
||||
#
|
||||
|
||||
@@ -54,8 +54,6 @@ use SourceChecker;
|
||||
#
|
||||
$| = 1;
|
||||
|
||||
print "Content-Type:text/html\n\n";
|
||||
|
||||
my @src_roots = getRepositoryList();
|
||||
|
||||
|
||||
@@ -65,6 +63,7 @@ my $filename = '';
|
||||
$filename = $::FORM{'file'} if defined($::FORM{'file'});
|
||||
if ($filename eq '')
|
||||
{
|
||||
print "Content-Type:text/html\n\n";
|
||||
&print_usage;
|
||||
exit;
|
||||
}
|
||||
@@ -91,6 +90,7 @@ if (defined $root && $root ne '') {
|
||||
if (-d $root) {
|
||||
unshift(@src_roots, $root);
|
||||
} else {
|
||||
print "Content-Type:text/html\n\n";
|
||||
&print_top;
|
||||
print "Error: Root, $root, is not a directory.<BR><BR>\n";
|
||||
print "</BODY></HTML>\n";
|
||||
@@ -113,6 +113,7 @@ foreach (@src_roots) {
|
||||
goto found_file if -r $rcs_filename;
|
||||
}
|
||||
# File not found
|
||||
print "Content-Type:text/html\n\n";
|
||||
&print_top;
|
||||
my $escaped_filename = html_quote($filename);
|
||||
print "Rcs file, $escaped_filename, does not exist.<BR><BR>\n";
|
||||
@@ -131,6 +132,15 @@ my $rcs_path;
|
||||
$revision = &parse_cvs_file($rcs_filename);
|
||||
my $file_rev = $revision;
|
||||
|
||||
my $start_rev;
|
||||
if ($browse_revtag eq 'HEAD') {
|
||||
$start_rev = $::head_revision; # $::head_revision is a global from cvsblame.pl
|
||||
} else {
|
||||
$start_rev = map_tag_to_revision($browse_revtag);
|
||||
}
|
||||
print "Content-Type:text/html\n";
|
||||
print "Last-Modified: ".time2str("%a, %d %b %Y %T %Z", str2time($::revision_ctime{$start_rev}), "GMT")."\n";
|
||||
print "\n";
|
||||
|
||||
# Handle the "mark" argument
|
||||
#
|
||||
@@ -254,12 +264,6 @@ print "$table_tag$table_header_tag";
|
||||
|
||||
# Print each line of the revision, preceded by its annotation.
|
||||
#
|
||||
my $start_rev;
|
||||
if ($browse_revtag eq 'HEAD') {
|
||||
$start_rev = $::head_revision; # $::head_revision is a global from cvsblame.pl
|
||||
} else {
|
||||
$start_rev = map_tag_to_revision($browse_revtag);
|
||||
}
|
||||
my $row_count = 0;
|
||||
my $max_rev_length = length($start_rev);
|
||||
my $max_author_length = 8;
|
||||
|
||||
@@ -53,9 +53,17 @@ sub sillyness {
|
||||
$zz = $::TreeList;
|
||||
$zz = $::file_description;
|
||||
$zz = $::principal_branch;
|
||||
$zz = $::revision_ctime;
|
||||
$zz = %::timestamp;
|
||||
}
|
||||
|
||||
my $request = new CGI;
|
||||
|
||||
sub http_die {
|
||||
print $request->header();
|
||||
die (@_);
|
||||
}
|
||||
|
||||
my $anchor_num = 0;
|
||||
my $font_tag = "";
|
||||
# Figure out which directory bonsai is in by looking at argv[0]
|
||||
@@ -66,7 +74,7 @@ if ($bonsaidir eq '') {
|
||||
$bonsaidir = '.';
|
||||
}
|
||||
|
||||
chdir $bonsaidir || die "Can't chdir to $bonsaidir";
|
||||
chdir $bonsaidir || http_die "Can't chdir to $bonsaidir";
|
||||
require 'CGI.pl';
|
||||
|
||||
my $cocommand = Param('cocommand');
|
||||
@@ -90,7 +98,6 @@ my $debug = 0;
|
||||
|
||||
my $MAX_REVS = 8;
|
||||
|
||||
|
||||
#
|
||||
# Make sure both kinds of standard output go to STDOUT.
|
||||
# XXX dup stdout onto stderr and flush stdout after the following prints
|
||||
@@ -102,270 +109,10 @@ my $MAX_REVS = 8;
|
||||
# exit;
|
||||
# }
|
||||
|
||||
|
||||
my $line_buffer;
|
||||
|
||||
# Consume one token from the already opened RCSFILE filehandle.
|
||||
# Unescape string tokens, if necessary.
|
||||
sub get_token {
|
||||
# Erase all-whitespace lines.
|
||||
while ($line_buffer =~ /^$/) {
|
||||
die ('Unexpected EOF') if eof(RCSFILE);
|
||||
$line_buffer = <RCSFILE>;
|
||||
$line_buffer =~ s/^\s+//; # Erase leading whitespace
|
||||
}
|
||||
|
||||
# A string of non-whitespace characters is a token ...
|
||||
return $1 if ($line_buffer =~ s/^([^;@][^;\s]*)\s*//o);
|
||||
|
||||
# ...and so is a single semicolon ...
|
||||
return ';' if ($line_buffer =~ s/^;\s*//o);
|
||||
|
||||
# ...or an RCS-encoded string that starts with an @ character.
|
||||
$line_buffer =~ s/^@([^@]*)//o;
|
||||
my $token = $1;
|
||||
|
||||
# Detect single @ character used to close RCS-encoded string.
|
||||
while ($line_buffer !~ /^@[^@]*$/o) {
|
||||
$token .= $line_buffer;
|
||||
die ('Unexpected EOF') if eof(RCSFILE);
|
||||
$line_buffer = <RCSFILE>;
|
||||
}
|
||||
|
||||
# Retain the remainder of the line after the terminating @ character.
|
||||
($line_buffer) = ($line_buffer =~ /^@\s*([^@]*)/o);
|
||||
|
||||
# Undo escape-coding of @ characters.
|
||||
$token =~ s/@@/@/og;
|
||||
|
||||
return $token;
|
||||
}
|
||||
|
||||
# Consume a token from RCS filehandle and ensure that it matches
|
||||
# the given string constant.
|
||||
sub match_token {
|
||||
my ($match) = @_;
|
||||
|
||||
my ($token) = &get_token;
|
||||
die ("Unexpected parsing error in RCS file.\n",
|
||||
"Expected token: $match, but saw: $token\n")
|
||||
if ($token ne $match);
|
||||
}
|
||||
|
||||
# Push RCS token back into the input buffer.
|
||||
sub unget_token {
|
||||
my ($token) = @_;
|
||||
$line_buffer = "$token $line_buffer";
|
||||
}
|
||||
|
||||
# Parses "administrative" header of RCS files, setting these globals:
|
||||
#
|
||||
# $::head_revision -- Revision for which cleartext is stored
|
||||
# $::principal_branch
|
||||
# $::file_description
|
||||
# %::revision_symbolic_name -- maps from numerical revision # to symbolic tag
|
||||
# %::tag_revision -- maps from symbolic tag to numerical revision #
|
||||
#
|
||||
sub parse_rcs_admin {
|
||||
my ($token, $tag, $tag_name, $tag_revision);
|
||||
my (@tags);
|
||||
|
||||
# Undefine variables, because we may have already read another RCS file
|
||||
undef %::tag_revision;
|
||||
undef %::revision_symbolic_name;
|
||||
|
||||
while (1) {
|
||||
# Read initial token at beginning of line
|
||||
$token = &get_token();
|
||||
|
||||
# We're done once we reach the description of the RCS tree
|
||||
if ($token =~ /^\d/o) {
|
||||
&unget_token($token);
|
||||
return;
|
||||
}
|
||||
|
||||
# print "token: $token\n";
|
||||
|
||||
if ($token eq 'head') {
|
||||
$::head_revision = &get_token;
|
||||
&get_token; # Eat semicolon
|
||||
} elsif ($token eq 'branch') {
|
||||
$::principal_branch = &get_token;
|
||||
&get_token; # Eat semicolon
|
||||
} elsif ($token eq 'symbols') {
|
||||
|
||||
# Create an associate array that maps from tag name to
|
||||
# revision number and vice-versa.
|
||||
while (($tag = &get_token) ne ';') {
|
||||
($tag_name, $tag_revision) = split(':', $tag);
|
||||
|
||||
$::tag_revision{$tag_name} = $tag_revision;
|
||||
$::revision_symbolic_name{$tag_revision} = $tag_name;
|
||||
}
|
||||
} elsif ($token eq 'comment') {
|
||||
$::file_description = &get_token;
|
||||
&get_token; # Eat semicolon
|
||||
|
||||
# Ignore all these other fields - We don't care about them.
|
||||
} elsif (($token eq 'locks') ||
|
||||
($token eq 'strict') ||
|
||||
($token eq 'expand') ||
|
||||
($token eq 'access')) {
|
||||
(1) while (&get_token ne ';');
|
||||
} else {
|
||||
warn ("Unexpected RCS token: $token\n");
|
||||
}
|
||||
}
|
||||
|
||||
die('Unexpected EOF');
|
||||
}
|
||||
|
||||
# Construct associative arrays that represent the topology of the RCS tree
|
||||
# and other arrays that contain info about individual revisions.
|
||||
#
|
||||
# The following associative arrays are created, keyed by revision number:
|
||||
# %::revision_date -- e.g. "96.02.23.00.21.52"
|
||||
# %::timestamp -- seconds since 12:00 AM, Jan 1, 1970 GMT
|
||||
# %::revision_author -- e.g. "tom"
|
||||
# %::revision_branches -- descendant branch revisions, separated by spaces,
|
||||
# e.g. "1.21.4.1 1.21.2.6.1"
|
||||
# %::prev_revision -- revision number of previous *ancestor* in RCS tree.
|
||||
# Traversal of this array occurs in the direction
|
||||
# of the primordial (1.1) revision.
|
||||
# %::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.
|
||||
# %::next_delta -- revision number of next "delta". Inverts %::prev_delta.
|
||||
#
|
||||
# Also creates %::last_revision, keyed by a branch revision number, which
|
||||
# indicates the latest revision on a given branch,
|
||||
# e.g. $::last_revision{"1.2.8"} == 1.2.8.5
|
||||
#
|
||||
sub parse_rcs_tree {
|
||||
my($revision, $date, $author, $branches, $next);
|
||||
my($branch, $is_trunk_revision);
|
||||
|
||||
# Undefine variables, because we may have already read another RCS file
|
||||
undef %::revision_date;
|
||||
undef %::timestamp;
|
||||
undef %::revision_author;
|
||||
undef %::revision_branches;
|
||||
undef %::prev_revision;
|
||||
undef %::prev_delta;
|
||||
undef %::next_delta;
|
||||
undef %::last_revision;
|
||||
|
||||
while (1) {
|
||||
$revision = &get_token;
|
||||
|
||||
# End of RCS tree description ?
|
||||
if ($revision eq 'desc') {
|
||||
&unget_token($revision);
|
||||
return;
|
||||
}
|
||||
|
||||
$is_trunk_revision = ($revision =~ /^[0-9]+\.[0-9]+$/);
|
||||
|
||||
$::tag_revision{$revision} = $revision;
|
||||
($branch) = $revision =~ /(.*)\.[0-9]+/o;
|
||||
$::last_revision{$branch} = $revision;
|
||||
|
||||
# Parse date
|
||||
&match_token('date');
|
||||
$date = &get_token;
|
||||
$::revision_date{$revision} = $date;
|
||||
&match_token(';');
|
||||
|
||||
# Convert date into timestamp
|
||||
# @date_fields = reverse(split(/\./, $date));
|
||||
# $date_fields[4]--; # Month ranges from 0-11, not 1-12
|
||||
# $::timestamp{$revision} = &timegm(@date_fields);
|
||||
|
||||
# Parse author
|
||||
&match_token('author');
|
||||
$author = &get_token;
|
||||
$::revision_author{$revision} = $author;
|
||||
&match_token(';');
|
||||
|
||||
# Parse state;
|
||||
&match_token('state');
|
||||
(1) while (&get_token ne ';');
|
||||
|
||||
# Parse branches
|
||||
&match_token('branches');
|
||||
$branches = '';
|
||||
my $token;
|
||||
while (($token = &get_token) ne ';') {
|
||||
$::prev_revision{$token} = $revision;
|
||||
$::prev_delta{$token} = $revision;
|
||||
$branches .= "$token ";
|
||||
}
|
||||
$::revision_branches{$revision} = $branches;
|
||||
|
||||
# Parse revision of next delta in chain
|
||||
&match_token('next');
|
||||
$next = '';
|
||||
if (($token = &get_token) ne ';') {
|
||||
$next = $token;
|
||||
&get_token; # Eat semicolon
|
||||
$::next_delta{$revision} = $next;
|
||||
$::prev_delta{$next} = $revision;
|
||||
if ($is_trunk_revision) {
|
||||
$::prev_revision{$revision} = $next;
|
||||
} else {
|
||||
$::prev_revision{$next} = $revision;
|
||||
}
|
||||
}
|
||||
|
||||
if ($debug > 1) {
|
||||
print "revision = $revision\n";
|
||||
print "date = $date\n";
|
||||
print "author = $author\n";
|
||||
print "branches = $branches\n";
|
||||
print "next = $next\n\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Reads and parses complete RCS file from already-opened RCSFILE descriptor.
|
||||
sub parse_rcs_file {
|
||||
my ($file) = @_;
|
||||
die("Couldn't open $file\n") if !open(RCSFILE, "< $file");
|
||||
$line_buffer = '';
|
||||
print "Reading RCS admin...\n" if ($debug);
|
||||
&parse_rcs_admin();
|
||||
print "Reading RCS revision tree topology...\n" if ($debug);
|
||||
&parse_rcs_tree();
|
||||
print "Done reading RCS file...\n" if ($debug);
|
||||
close(RCSFILE);
|
||||
}
|
||||
|
||||
# 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.
|
||||
sub map_tag_to_revision {
|
||||
my($tag_or_revision) = @_;
|
||||
|
||||
my ($revision) = $::tag_revision{$tag_or_revision};
|
||||
|
||||
# Is this a branch tag, e.g. xxx.yyy.0.zzz
|
||||
if ($revision =~ /(.*)\.0\.([0-9]+)/o) {
|
||||
my $branch = $1 . '.' . $2;
|
||||
# Return latest revision on the branch, if any.
|
||||
return $::last_revision{$branch} if (defined($::last_revision{$branch}));
|
||||
return $1; # No revisions on branch - return branch point
|
||||
} else {
|
||||
return $revision;
|
||||
}
|
||||
}
|
||||
|
||||
require 'cvsblame.pl';
|
||||
#
|
||||
# Print HTTP content-type header and the header-delimiting extra newline.
|
||||
#
|
||||
my $request = new CGI;
|
||||
print $request->header();
|
||||
|
||||
my $request_method = $request->request_method(); # e.g., "GET", "POST", etc.
|
||||
my $script_name = $ENV{'SCRIPT_NAME'};
|
||||
@@ -403,14 +150,14 @@ my $deletion_bg_color = 'LightGreen';
|
||||
my $diff_bg_color = 'White';
|
||||
|
||||
# Ensure that necessary arguments are present
|
||||
die("command not defined in URL\n") if $opt_command eq '';
|
||||
die("command $opt_command: subdir not defined\n") if $opt_subdir eq '';
|
||||
http_die("command not defined in URL\n") if $opt_command eq '';
|
||||
http_die("command $opt_command: subdir not defined\n") if $opt_subdir eq '';
|
||||
if ($opt_command eq 'DIFF' ||
|
||||
$opt_command eq 'DIFF_FRAMESET' ||
|
||||
$opt_command eq 'DIFF_LINKS') {
|
||||
die("command $opt_command: file not defined in URL\n") if $opt_file eq '';
|
||||
die("command $opt_command: rev1 not defined in URL\n") if $opt_rev1 eq '';
|
||||
die("command $opt_command: rev2 not defined in URL\n") if $opt_rev2 eq '';
|
||||
http_die("command $opt_command: file not defined in URL\n") if $opt_file eq '';
|
||||
http_die("command $opt_command: rev1 not defined in URL\n") if $opt_rev1 eq '';
|
||||
http_die("command $opt_command: rev2 not defined in URL\n") if $opt_rev2 eq '';
|
||||
|
||||
}
|
||||
|
||||
@@ -455,10 +202,20 @@ if (!$found) {
|
||||
exit;
|
||||
}
|
||||
|
||||
sub http_lastmod {
|
||||
&parse_cvs_file($dir.'/'.$opt_file.',v');
|
||||
my $lm=str2time($::revision_ctime{$opt_rev1});
|
||||
my $lm2=str2time($::revision_ctime{$opt_rev2});
|
||||
$lm = $lm2 if $lm2 > $lm;
|
||||
print "Last-Modified: ".time2str("%a, %d %b %Y %T %Z", $lm, "GMT")."\n";
|
||||
print $request->header();
|
||||
print "\n";
|
||||
}
|
||||
|
||||
# Create top-level frameset document.
|
||||
sub do_diff_frameset {
|
||||
chdir($dir);
|
||||
|
||||
http_lastmod;
|
||||
print "<TITLE>$opt_file: $opt_rev1 vs. $opt_rev2</TITLE>\n";
|
||||
print "<FRAMESET ROWS='*,90' FRAMESPACING=0 BORDER=1>\n";
|
||||
|
||||
@@ -476,7 +233,7 @@ sub do_diff_frameset {
|
||||
|
||||
# Create links to document created by DIFF command.
|
||||
sub do_diff_links {
|
||||
|
||||
http_lastmod;
|
||||
print qq%
|
||||
<HEAD>
|
||||
<SCRIPT $::script_type><!--
|
||||
@@ -615,7 +372,7 @@ sub guess_tab_width {
|
||||
|
||||
# Create gdiff-like output.
|
||||
sub do_diff {
|
||||
|
||||
http_lastmod;
|
||||
print "<HTML><HEAD>";
|
||||
print "<TITLE>$opt_file: $opt_rev1 vs. $opt_rev2</TITLE>\n";
|
||||
print "</HEAD>";
|
||||
@@ -637,6 +394,7 @@ sub do_diff {
|
||||
|
||||
# Show specified CVS log entry.
|
||||
sub do_log {
|
||||
http_lastmod;
|
||||
print "<TITLE>$opt_file: $opt_rev CVS log entry</TITLE>\n";
|
||||
print '<PRE>';
|
||||
|
||||
@@ -713,7 +471,7 @@ sub do_directory {
|
||||
my $first_rev;
|
||||
if ($opt_branch) {
|
||||
$first_rev = &map_tag_to_revision($opt_branch);
|
||||
die("$0: error: -r: No such revision: $opt_branch\n")
|
||||
http_die("$0: error: -r: No such revision: $opt_branch\n")
|
||||
if ($first_rev eq '');
|
||||
} else {
|
||||
$first_rev = $::head_revision;
|
||||
|
||||
Reference in New Issue
Block a user