=================================================================== RCS file: /cvs/cvsweb/cvsweb.cgi,v retrieving revision 1.1.1.6 retrieving revision 1.1.1.13 diff -u -p -r1.1.1.6 -r1.1.1.13 --- cvsweb/cvsweb.cgi 2000/09/19 20:14:40 1.1.1.6 +++ cvsweb/cvsweb.cgi 2000/12/07 12:45:50 1.1.1.13 @@ -42,8 +42,8 @@ # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # -# $zId: cvsweb.cgi,v 1.101 2000/09/13 22:44:05 jumager Exp $ -# $kId: cvsweb.cgi,v 1.24 2000/09/19 20:07:16 knu Exp $ +# $zId: cvsweb.cgi,v 1.104 2000/11/01 22:05:12 hnordstrom Exp $ +# $kId: cvsweb.cgi,v 1.41 2000/12/06 18:19:12 knu Exp $ # ### @@ -52,6 +52,7 @@ use strict; use vars qw ( $config $allow_version_select $verbose %CVSROOT %CVSROOTdescr %MIRRORS %DEFAULTVALUE %ICONS %MTYPES + @DIFFTYPES %DIFFTYPES @LOGSORTKEYS %LOGSORTKEYS %alltags @tabcolors %fileinfo %tags @branchnames %nameprinted %symrev %revsym @allrevisions %date %author @revdisplayorder @revisions %state %difflines %log %branchpoint @revorder @@ -59,13 +60,14 @@ use vars qw ( $checkoutMagic $doCheckout $scriptname $scriptwhere $where $pathinfo $Browser $nofilelinks $maycompress @stickyvars %funcline_regexp $is_mod_perl - $is_lynx $is_w3m $is_msie $is_mozilla3 $is_textbased + $is_links $is_lynx $is_w3m $is_msie $is_mozilla3 $is_textbased %input $query $barequery $sortby $bydate $byrev $byauthor - $bylog $byfile $hr_default $logsort $cvstree $cvsroot - $mimetype $defaultTextPlain $defaultViewable $allow_compress - $GZIPBIN $backicon $diricon $fileicon $fullname $newname - $cvstreedefault $body_tag $logo $defaulttitle $address - $backcolor $long_intro $short_instruction $shortLogLen + $bylog $byfile $defaultDiffType $logsort $cvstree $cvsroot + $mimetype $charset $defaultTextPlain $defaultViewable + $allow_compress $GZIPBIN $backicon $diricon $fileicon + $fullname $newname $cvstreedefault + $body_tag $body_tag_for_src $logo $defaulttitle $address + $long_intro $short_instruction $shortLogLen $show_author $dirtable $tablepadding $columnHeaderColorDefault $columnHeaderColorSorted $hr_breakable $showfunc $hr_ignwhite $hr_ignkeysubst $diffcolorHeading $diffcolorEmpty $diffcolorRemove @@ -73,15 +75,19 @@ use vars qw ( $difffontsize $inputTextSize $mime_types $allow_annotate $allow_markup $use_java_script $open_extern_window $extern_window_width $extern_window_height $edit_option_form - $checkout_magic $show_subdir_lastmod $show_log_in_markup $v + $show_subdir_lastmod $show_log_in_markup $v $navigationHeaderColor $tableBorderColor $markupLogColor $tabstop $state $annTable $sel $curbranch @HideModules $module $use_descriptions %descriptions @mytz $dwhere $moddate $use_moddate $has_zlib $gzip_open + $LOG_FILESEPARATOR $LOG_REVSEPARATOR ); sub printDiffSelect($); +sub printDiffLinks($$); +sub printLogSortSelect($); sub findLastModifiedSubdirs(@); +sub htmlify_sub(&$); sub htmlify($;$); sub spacedHtmlText($;$); sub link($$); @@ -114,6 +120,8 @@ sub download_url($$;$); sub download_link($$$;$); sub toggleQuery($$); sub urlencode($); +sub htmlquote($); +sub htmlunquote($); sub http_header(;$); sub html_header($); sub html_footer(); @@ -124,14 +132,13 @@ sub forbidden_module($); use Cwd; # == EDIT this == -# User configuration is stored in -$config = undef; - -for ($ENV{CVSWEB_CONFIG}, -# '/home/knu/etc/cvsweb.conf', +# Locations to search for user configuration, in order: +for ( + $ENV{CVSWEB_CONFIG}, '/usr/local/etc/cvsweb.conf', - getcwd . '/cvsweb.conf') { - $config = $_ if defined($_) && -r $_; + getcwd() . '/cvsweb.conf' + ) { + $config = $_ if defined($_) && -r $_; } # == Configuration defaults == @@ -145,8 +152,9 @@ $allow_version_select = 1; # These are defined to allow checking with perl -cw %CVSROOT = %MIRRORS = %DEFAULTVALUE = %ICONS = %MTYPES = %tags = %alltags = @tabcolors = (); -$cvstreedefault = $body_tag = $logo = $defaulttitle = $address = -$backcolor = $long_intro = $short_instruction = $shortLogLen = +$cvstreedefault = $body_tag = $body_tag_for_src = +$logo = $defaulttitle = $address = +$long_intro = $short_instruction = $shortLogLen = $show_author = $dirtable = $tablepadding = $columnHeaderColorDefault = $columnHeaderColorSorted = $hr_breakable = $showfunc = $hr_ignwhite = $hr_ignkeysubst = $diffcolorHeading = $diffcolorEmpty = $diffcolorRemove = @@ -154,10 +162,56 @@ $diffcolorChange = $diffcolorAdd = $diffcolorDarkChang $difffontsize = $inputTextSize = $mime_types = $allow_annotate = $allow_markup = $use_java_script = $open_extern_window = $extern_window_width = $extern_window_height = $edit_option_form = -$checkout_magic = $show_subdir_lastmod = $show_log_in_markup = $v = +$show_subdir_lastmod = $show_log_in_markup = $v = $navigationHeaderColor = $tableBorderColor = $markupLogColor = -$tabstop = $use_moddate = $moddate = undef; +$tabstop = $use_moddate = $moddate = $gzip_open = undef; +$LOG_FILESEPARATOR = q/^={77}$/; +$LOG_REVSEPARATOR = q/^-{28}$/; + +@DIFFTYPES = qw(h H u c s); +@DIFFTYPES{@DIFFTYPES} = ( + { + 'descr' => 'colored', + 'opts' => [ '-u' ], + 'colored' => 1, + }, + { + 'descr' => 'long colored', + 'opts' => [ '--unified=15' ], + 'colored' => 1, + }, + { + 'descr' => 'unified', + 'opts' => [ '-u' ], + 'colored' => 0, + }, + { + 'descr' => 'context', + 'opts' => [ '-c' ], + 'colored' => 0, + }, + { + 'descr' => 'side by side', + 'opts' => [ '--side-by-side', '--width=164' ], + 'colored' => 0, + }, + ); + +@LOGSORTKEYS = qw(cvs date rev); +@LOGSORTKEYS{@LOGSORTKEYS} = ( + { + 'descr' => 'Not sorted', + }, + { + 'descr' => 'Commit date', + }, + { + 'descr' => 'Revision', + }, + ); + + ##### End of configuration variables ##### use Time::Local; @@ -191,12 +245,13 @@ $is_mod_perl = defined($ENV{MOD_PERL}); # per file, so disable the link at the icon # in this case: $Browser = $ENV{HTTP_USER_AGENT}; +$is_links = ($Browser =~ m`^Links `); $is_lynx = ($Browser =~ m`^Lynx/`i); $is_w3m = ($Browser =~ m`^w3m/`i); $is_msie = ($Browser =~ m`MSIE`); $is_mozilla3 = ($Browser =~ m`^Mozilla/[3-9]`); -$is_textbased = ($is_lynx || $is_w3m); +$is_textbased = ($is_links || $is_lynx || $is_w3m); $nofilelinks = $is_textbased; @@ -209,7 +264,8 @@ $nofilelinks = $is_textbased; # display garbage then :-/ # Turn off gzip if running under mod_perl and no zlib is available, # piping does not work as expected inside the server. -$maycompress = (($ENV{HTTP_ACCEPT_ENCODING} =~ m`gzip` +$maycompress = (((defined($ENV{HTTP_ACCEPT_ENCODING}) + && $ENV{HTTP_ACCEPT_ENCODING} =~ m`gzip`) || $is_mozilla3) && !$is_msie && !($is_mod_perl && !$has_zlib)); @@ -221,9 +277,11 @@ $maycompress = (($ENV{HTTP_ACCEPT_ENCODING} =~ m`gzip` @stickyvars = qw(cvsroot hideattic sortby logsort f only_with_tag); if (-f $config) { - do $config; -} -else { + do $config + || &fatal("500 Internal Error", + sprintf('Error in loading configuration file: %s

%s
', + $config, &htmlify($@))); +} else { &fatal("500 Internal Error", 'Configuration not found. Set the variable $config ' . 'in cvsweb.cgi, or the environment variable ' @@ -275,25 +333,24 @@ foreach (keys %DEFAULTVALUE) } $barequery = ""; +my @barequery; foreach (@stickyvars) { # construct a query string with the sticky non default parameters set if (defined($input{$_}) && $input{$_} ne '' && !(defined($DEFAULTVALUE{$_}) && $input{$_} eq $DEFAULTVALUE{$_})) { - if ($barequery) { - $barequery = $barequery . "&"; - } - my $thisval = urlencode($_) . "=" . urlencode($input{$_}); - $barequery .= $thisval; + push @barequery, join('=', urlencode($_), urlencode($input{$_})); } } # is there any query ? -if ($barequery) { +if (@barequery) { + $barequery = join('&', @barequery); $query = "?$barequery"; - $barequery = "&" . $barequery; + $barequery = "&$barequery"; } else { $query = ""; } +undef @barequery; # get actual parameters $sortby = $input{"sortby"}; @@ -318,7 +375,7 @@ else { $byfile = 1; } -$hr_default = $input{'f'} eq 'h'; +$defaultDiffType = $input{'f'}; $logsort = $input{'logsort'}; @@ -341,19 +398,30 @@ if ($input{'cvsroot'} && $CVSROOT{$input{'cvsroot'}}) $cvsroot = $CVSROOT{$cvstree}; # create icons out of description -foreach my $k (keys %ICONS) { +my $k; +foreach $k (keys %ICONS) { no strict 'refs'; my ($itxt,$ipath,$iwidth,$iheight) = @{$ICONS{$k}}; if ($ipath) { - ${"${k}icon"} = "\"$itxt\""; + ${"${k}icon"} = sprintf('%s', + htmlquote($ipath), htmlquote($itxt), $iwidth, $iheight) } else { ${"${k}icon"} = $itxt; } } +undef $k; +my $config_cvstree = "$config-$cvstree"; + # Do some special configuration for cvstrees -do "$config-$cvstree" if (-f "$config-$cvstree"); +if (-f $config_cvstree) { + do $config_cvstree + || &fatal("500 Internal Error", + sprintf('Error in loading configuration file: %s

%s
', + $config_cvstree, &htmlify($@))); +} +undef $config_cvstree; $prcategories = '(?:' . join('|', @prcategories) . ')'; $prcgi .= '%s' if defined($prcgi) && $prcgi !~ /%s/; @@ -462,57 +530,63 @@ elsif (-d $fullname) { } print "\n"; $infocols++; - print ""; # do not display the other column-headers, if we do not have any files # with revision information: if (scalar(%fileinfo)) { $infocols++; - print ""; $infocols++; - print ""; if ($show_author) { $infocols++; - print ""; } $infocols++; - print ""; } elsif ($use_descriptions) { - print "
"; - print "" if (!$byfile); - print "File"; - print "" if (!$byfile); + printf '
', + $byfile ? $columnHeaderColorSorted : $columnHeaderColorDefault; + if ($byfile) { + print 'File'; + } else { + print &link('File', sprintf('./%s#dirlist', + &toggleQuery("sortby", "file"))); + } print ""; - print "" if (!$byrev); - print "Rev."; - print "" if (!$byrev); + printf '', + $byrev ? $columnHeaderColorSorted : $columnHeaderColorDefault; + if ($byrev) { + print 'Rev.'; + } else { + print &link('Rev.', sprintf('./%s#dirlist', + &toggleQuery("sortby", "rev"))); + } print ""; - print "" if (!$bydate); - print "Age"; - print "" if (!$bydate); + printf '', + $bydate ? $columnHeaderColorSorted : $columnHeaderColorDefault; + if ($bydate) { + print 'Age'; + } else { + print &link('Age', sprintf('./%s#dirlist', + &toggleQuery("sortby", "date"))); + } print ""; - print "" if (!$byauthor); - print "Author"; - print "" if (!$byauthor); + printf '', + $byauthor ? $columnHeaderColorSorted : $columnHeaderColorDefault; + if ($byauthor) { + print 'Author'; + } else { + print &link('Author', sprintf('./%s#dirlist', + &toggleQuery("sortby", "author"))); + } print ""; - print "" if (!$bylog); - print "Last log entry"; - print "" if (!$bylog); + printf '', + $bylog ? $columnHeaderColorSorted : $columnHeaderColorDefault; + if ($bylog) { + print 'Last log entry'; + } else { + print &link('Last log entry', sprintf('./%s#dirlist', + &toggleQuery("sortby", "log"))); + } print ""; + printf '', $columnHeaderColorDefault; print "Description"; $infocols++; } @@ -537,9 +611,9 @@ elsif (-d $fullname) { closedir($dh); } - my $hideAtticToggleLink = "[Hide]" if (!$input{'hideattic'}); + my $hideAtticToggleLink = $input{'hideattic'} ? '' : + &link('[Hide]', sprintf('./%s#dirlist', + &toggleQuery ("hideattic"))); # Sort without the Attic/ pathname. # place directories first @@ -577,38 +651,38 @@ elsif (-d $fullname) { next if ($_ eq '..' && $where eq '/'); my ($rev,$date,$log,$author,$filename) = @{$fileinfo{$_}} if (defined($fileinfo{$_})); - print "
" if ($dirtable); + printf '
', $tabcolors[$dirrow % 2] if $dirtable; if ($_ eq '..') { - $url = "../" . $query; + $url = "../$query"; if ($nofilelinks) { print $backicon; } else { - print &link($backicon,$url); + print &link($backicon, $url); } - print " ", &link("Previous Directory",$url); + print " ", &link("Previous Directory", $url); } else { - $url = urlencode($_) . '/' . $query; + $url = urlencode($_) . "/$query"; print ""; if ($nofilelinks) { print $diricon; } else { - print &link($diricon,$url); + print &link($diricon, $url); } - print " ", &link($_ . "/", $url), $attic; + print " ", &link("$_/", $url), $attic; if ($_ eq "Attic") { - print "  [Don't hide]"; + print "  "; + print &link("[Don't hide]", sprintf('./%s#dirlist', + &toggleQuery ("hideattic"))); } } # Show last change in dir if ($filename) { print "  " if ($dirtable); if ($date) { - print " " . readableTime(time() - $date,0) . ""; + print " ", readableTime(time() - $date,0), ""; } if ($show_author) { print " " if ($dirtable); @@ -619,8 +693,8 @@ elsif (-d $fullname) { print "$filename/$rev"; print "
" if ($dirtable); if ($log) { - print " " - . &htmlify(substr($log,0,$shortLogLen)); + print " ", + &htmlify(substr($log,0,$shortLogLen)); if (length $log > 80) { print "..."; } @@ -630,7 +704,7 @@ elsif (-d $fullname) { else { my ($dwhere) = ($where ne "/" ? $where : "") . $_; if ($use_descriptions && defined $descriptions{$dwhere}) { - print "
 " if $dirtable; + print " " if $dirtable; print $descriptions{$dwhere}; } elsif ($dirtable && $infocols > 1) { # close the row with the appropriate number of @@ -661,7 +735,7 @@ elsif (-d $fullname) { next if (!defined($fileinfo{$_})); ($rev,$date,$log,$author) = @{$fileinfo{$_}}; $filesfound++; - print "
" if ($dirtable); + printf '
', $tabcolors[$dirrow % 2] if $dirtable; print ""; if ($nofilelinks) { print $fileicon; @@ -672,11 +746,11 @@ elsif (-d $fullname) { print " ", &link($_, $url), $attic; print " " if ($dirtable); download_link($fileurl, - $rev, $rev, - $defaultViewable ? "text/x-cvsweb-markup" : undef); + $rev, $rev, + $defaultViewable ? "text/x-cvsweb-markup" : undef); print " " if ($dirtable); if ($date) { - print " " . readableTime(time() - $date,0) . ""; + print " ", readableTime(time() - $date,0), ""; } if ($show_author) { print " " if ($dirtable); @@ -684,7 +758,7 @@ elsif (-d $fullname) { } print " " if ($dirtable); if ($log) { - print " " . &htmlify(substr($log,0,$shortLogLen)); + print " ", &htmlify(substr($log,0,$shortLogLen)); if (length $log > 80) { print "..."; } @@ -699,7 +773,7 @@ elsif (-d $fullname) { if ($dirtable && defined($tableBorderColor)) { print "
"; } - print "". ($dirtable == 1) ? "" : "" . "\n"; + print( $dirtable == 1 ? "\n" : "\n" ); if ($filesexists && !$filesfound) { print "

NOTE: There are $filesexists files, but none matches the current tag ($input{only_with_tag})\n"; @@ -757,12 +831,9 @@ elsif (-d $fullname) { print "Revision"; print "Log message"; print ""; - print "revisions by: \n"; - print ""; + print "Sort log by: "; + printLogSortSelect(0); + print ""; print "Diff format: "; printDiffSelect(0); print ""; @@ -782,18 +853,18 @@ elsif (-d $fullname) { elsif (-f $fullname . ',v') { if (defined($input{'rev'}) || $doCheckout) { &doCheckout($fullname, $input{'rev'}); - close(GZIP) if ($gzip_open); + gzipclose(); exit; } if (defined($input{'annotate'}) && $allow_annotate) { &doAnnotate($input{'annotate'}); - close(GZIP) if ($gzip_open); + gzipclose(); exit; } if (defined($input{'r1'}) && defined($input{'r2'})) { &doDiff($fullname, $input{'r1'}, $input{'tr1'}, $input{'r2'}, $input{'tr2'}, $input{'f'}); - close(GZIP) if ($gzip_open); + gzipclose(); exit; } print("going to dolog($fullname)\n") if ($verbose); @@ -816,7 +887,7 @@ elsif (-d $fullname) { # e.g. foo.c &doDiff($fullname, $input{'r1'}, $input{'tr1'}, $input{'r2'}, $input{'tr2'}, $input{'f'}); - close(GZIP) if ($gzip_open); + gzipclose(); exit; } elsif (($newname = $fullname) =~ s|/([^/]+)$|/Attic/$1| && @@ -851,23 +922,48 @@ elsif (-d $fullname) { &fatal("404 Not Found","$where: no such file or directory"); } -close(GZIP) if ($gzip_open); +gzipclose(); ## End MAIN sub printDiffSelect($) { my ($use_java_script) = @_; - my ($f) = $input{'f'}; - print "'; + + local $_; + for (@DIFFTYPES) { + printf('

Annotation of $pathname$filename, Revision $rev

\n"; - # this seems to be necessary $| = 1; $| = 0; # Flush @@ -1144,7 +1294,7 @@ sub doAnnotate($$) { # the public domain. # we could abandon the use of rlog, rcsdiff and co using # the cvsserver in a similiar way one day (..after rewrite) - $pid = open2($reader, $writer, "cvs server") || fatal ("500 Internal Error", + $pid = open2($reader, $writer, "cvs -Rl server") || fatal ("500 Internal Error", "Fatal Error - unable to open cvs for annotation"); # OK, first send the request to the server. A simplified example is: @@ -1196,6 +1346,11 @@ sub doAnnotate($$) { # were nicer about buffering, then we could just leave it open, I think. close ($writer) || die "cannot close: $!"; + http_header(); + + navigateHeader($scriptwhere,$pathname,$filename,$rev, "annotate"); + print "

Annotation of $pathname$filename, Revision $rev

\n"; + # Ready to get the responses from the server. # For example: # E Annotations for foo/xx @@ -1228,40 +1383,48 @@ sub doAnnotate($$) { } elsif ($words[0] eq "M") { $lineNr++; - my $lrev = substr ($_, 2, 13); - my $lusr = substr ($_, 16, 9); - my $line = substr ($_, 36); + (my $lrev = substr($_, 2, 13)) =~ y/ //d; + (my $lusr = substr($_, 16, 9)) =~ y/ //d; + my $line = substr($_, 36); + my $isCurrentRev = ($rev eq $lrev); # we should parse the date here .. if ($lrev eq $oldLrev) { - $revprint = " "; + $revprint = sprintf('%-8s', ''); } else { - $revprint = $lrev; $oldLusr = ""; - $revprint =~ s`^(\S+)`$1`; # ` + $revprint = sprintf('%-8s', $lrev); + $revprint =~ s`\S+`&link($&, "$scriptwhere$query#rev$&")`e; # ` + $oldLusr = ''; } if ($lusr eq $oldLusr) { - $usrprint = " "; + $usrprint = ''; } else { $usrprint = $lusr; } $oldLrev = $lrev; $oldLusr = $lusr; - # is there a less timeconsuming way to strip spaces ? - ($lrev = $lrev) =~ s/\s+//g; - my $isCurrentRev = ($rev eq $lrev); - print "" if ($isCurrentRev); - printf ("%8s%s%8s %4d:", $revprint, ($isCurrentRev ? "|" : " "), $usrprint, $lineNr); + # Set bold for text-based browsers only - graphical + # browsers show bold fonts a bit wider than regular fonts, + # so it looks irregular. + print "" if ($isCurrentRev && $is_textbased); + + printf "%s%s %-8s %4d:", + $revprint, + $isCurrentRev ? '!' : ' ', + $usrprint, + $lineNr; print spacedHtmlText($line, $d{'tabstop'}); - print "" if ($isCurrentRev); + + print "" if ($isCurrentRev && $is_textbased); } elsif ($words[0] eq "ok") { # We could complain about any text received after this, like the # CVS command line client. But for simplicity, we don't. } elsif ($words[0] eq "error") { - fatal ("500 Internal Error", "Error occured during annotate: $_"); + fatal("500 Internal Error", "Error occured during annotate: $_"); } } if ($annTable) { @@ -1326,6 +1489,11 @@ sub doCheckout($$) { open(STDERR, ">&STDOUT"); # Redirect stderr to stdout exec("cvs", "-Rld", $cvsroot, "co", "-p", $revopt, $where); } + + if (eof($fh)) { + &fatal("404 Not Found", + "$where is not (any longer) pertinent"); + } #=================================================================== #Checking out squid/src/ftp.c #RCS: /usr/src/CVS/squid/src/ftp.c,v @@ -1345,12 +1513,7 @@ sub doCheckout($$) { } if ($filename ne $where) { &fatal("500 Internal Error", - "Unexpected output from cvs co: $cvsheader" - . "

Check whether the directory $cvsroot/CVSROOT exists " - . "and the script has write-access to the CVSROOT/history " - . "file if it exists." - . "
The script needs to place lock files in the " - . "directory the file is in as well.
"); + "Unexpected output from cvs co: $cvsheader"); } $| = 1; @@ -1378,12 +1541,14 @@ sub cvswebMarkup($$$) { print "


"; print "
"; print "File: ", &clickablePath($where, 1); - print " "; - &download_link(urlencode($fileurl), $revision, "(download)"); + print " ("; + &download_link($fileurl, $revision, "download"); + print ")"; if (!$defaultTextPlain) { - print " "; - &download_link(urlencode($fileurl), $revision, "(as text)", + print " ("; + &download_link($fileurl, $revision, "as text", "text/plain"); + print ")"; } print "
\n"; if ($show_log_in_markup) { @@ -1399,10 +1564,10 @@ sub cvswebMarkup($$$) { my $url = download_url($fileurl, $revision, $mimetype); print "
"; if ($mimetype =~ /^image/) { - print "
"; + printf '
', htmlquote("$url$barequery"); } elsif ($mimetype =~ m%^application/pdf%) { - print "
"; + printf '
', htmlquote("$url$barequery"); } else { print "
";
@@ -1433,7 +1598,7 @@ sub viewable($) {
 sub doDiff($$$$$$) {
 	my($fullname, $r1, $tr1, $r2, $tr2, $f) = @_;
         my $fh = do {local(*FH);};
-	my ($rev1, $rev2, $sym1, $sym2, @difftype, $diffname, $f1, $f2);
+	my ($rev1, $rev2, $sym1, $sym2, $f1, $f2);
 
 	if ($r1 =~ /([^:]+)(:(.+))?/) {
 	    $rev1 = $1;
@@ -1467,36 +1632,18 @@ sub doDiff($$$$$$) {
 	    ($rev1, $sym1) = ($rev2, $sym2);
 	    ($rev2, $sym2) = ($tmp1, $tmp2);
 	}
-	my $human_readable = 0;
-	if ($f eq 'c') {
-	    @difftype = qw{-c};
-	    $diffname = "Context diff";
-	}
-	elsif ($f eq 's') {
-	    @difftype = qw{--side-by-side --width=164};
-	    $diffname = "Side by Side";
-	}
-	elsif ($f eq 'H') {
-	    $human_readable = 1;
-	    @difftype = qw{--unified=15};
-	    $diffname = "Long Human readable";
-	}
-	elsif ($f eq 'h') {
-	    @difftype =qw{-u};
-	    $human_readable = 1;
-	    $diffname = "Human readable";
-	}
-	elsif ($f eq 'u') {
-	    @difftype = qw{-u};
-	    $diffname = "Unidiff";
-	}
-	else {
+	my $difftype = $DIFFTYPES{$f};
+
+	if (!$difftype) {
 	    fatal ("400 Bad arguments", "Diff format $f not understood");
 	}
 
+	my @difftype       = @{$difftype->{'opts'}};
+	my $human_readable = $difftype->{'colored'};
+
 	# apply special options
 	if ($showfunc) {
-	    push @difftype, '-p' if $f =~ /^[cHhu]$/;
+	    push @difftype, '-p' if $f ne 's';
 
 	    my($re1, $re2);
 
@@ -1522,7 +1669,7 @@ sub doDiff($$$$$$) {
 	if ($human_readable) {
 	    http_header();
 	    &human_readable_diff($fh, $rev2);
-	    close(GZIP) if ($gzip_open);
+	    gzipclose();
 	    exit;
 	}
 	else {
@@ -1558,14 +1705,14 @@ sub doDiff($$$$$$) {
 		s|$cvsroot/||o;
 		if ($sym1) {
 		    chop;
-		    $_ .= " " . $sym1 . "\n";
+		    $_ .= " $sym1\n";
 		}
 	    }
 	    elsif (m|^$f2 $cvsroot|o) {
 		s|$cvsroot/||o;
 		if ($sym2) {
 		    chop;
-		    $_ .= " " . $sym2 . "\n";
+		    $_ .= " $sym2\n";
 		}
 	    }
 	    print $_;
@@ -1633,64 +1780,67 @@ sub getDirLogs($$@) {
 again:
 	if ($state eq "head") {
 	    #$rcsfile = $1 if (/^RCS file: (.+)$/); #not used (yet)
-	    $filename = $1 if (/^Working file: (.+)$/);
-	    $head = $1 if (/^head: (.+)$/);
-	    $branch = $1 if (/^branch: (.+)$/);
-	}
-	if ($state eq "head" && /^symbolic names/) {
-	    $state = "tags";
-	    ($branch = $head) =~ s/\.\d+$// if (!defined($branch));
-	    $branch =~ s/(\.?)(\d+)$/${1}0.$2/;
-	    $symrev{MAIN} = $branch;
-	    $symrev{HEAD} = $branch;
-	    $alltags{MAIN} = 1;
-	    $alltags{HEAD} = 1;
-	    push (@filetags, "MAIN", "HEAD");
+
+	    if (/^Working file: (.+)$/) {
+		$filename = $1;
+	    } elsif (/^head: (.+)$/) {
+		$head = $1;
+	    } elsif (/^branch: (.+)$/) {
+		$branch = $1 
+	    } elsif (/^symbolic names:/) {
+		$state = "tags";
+		($branch = $head) =~ s/\.\d+$// if (!defined($branch));
+		$branch =~ s/(\.?)(\d+)$/${1}0.$2/;
+		$symrev{MAIN} = $branch;
+		$symrev{HEAD} = $branch;
+		$alltags{MAIN} = 1;
+		$alltags{HEAD} = 1;
+		push (@filetags, "MAIN", "HEAD");
+	    } elsif (/$LOG_REVSEPARATOR/o) {
+		$state = "log";
+		$rev = undef;
+		$date = undef;
+		$log = "";
+		# Try to reconstruct the relative filename if RCS spits out a full path
+		$filename =~ s%^\Q$DirName\E/%%;
+	    }
 	    next;
 	}
-	if ($state eq "tags" &&
-			    /^\s+(.+):\s+([\d\.]+)\s+$/) {
-	    push (@filetags, $1);
-	    $symrev{$1} = $2;
-	    $alltags{$1} = 1;
-	    next;
-	}
-	if ($state eq "tags" && /^\S/) {
-	    if (defined($tag) && (defined($symrev{$tag}) || $tag eq "HEAD")) {
-		$revwanted = $tag eq "HEAD" ? $symrev{"MAIN"} : $symrev{$tag};
-		($branch = $revwanted) =~ s/\b0\.//;
-		($branchpoint = $branch) =~ s/\.?\d+$//;
-		$revwanted = undef if ($revwanted ne $branch);
-	    }
-	    elsif (defined($tag) && $tag ne "HEAD") {
-		print "Tag not found, skip this file" if ($verbose);
-		$state = "skip";
+	if ($state eq "tags") {
+	    if (/^\s+(.+):\s+([\d\.]+)\s+$/) {
+		push (@filetags, $1);
+		$symrev{$1} = $2;
+		$alltags{$1} = 1;
 		next;
+	    } elsif (/^\S/) {
+		if (defined($tag)) {
+		    if(defined($symrev{$tag}) || $tag eq "HEAD") {
+			$revwanted = $symrev{$tag eq "HEAD" ? "MAIN" : $tag};
+			($branch = $revwanted) =~ s/\.0\././;
+			($branchpoint = $branch) =~ s/\.?\d+$//;
+			$revwanted = undef if ($revwanted ne $branch);
+#print "\n[revwanted=$revwanted]";
+		    } elsif ($tag ne "HEAD") {
+			print "Tag not found, skip this file" if ($verbose);
+			$state = "skip";
+			next;
+		    }
+		}
+		foreach my $tagfound (@filetags) {
+		    $tags{$tagfound} = 1;
+		}
+		$state = "head";
+		goto again;
 	    }
-	    foreach my $tagfound (@filetags) {
-		$tags{$tagfound} = 1;
-	    }
-	    $state = "head";
-	    goto again;
 	}
-	if ($state eq "head" && /^----------------------------$/) {
-	    $state = "log";
-	    $rev = undef;
-	    $date = undef;
-	    $log = "";
-	    # Try to reconstruct the relative filename if RCS spits out a full path
-	    $filename =~ s%^\Q$DirName\E/%%;
-	    next;
-	}
 	if ($state eq "log") {
-	    if (/^----------------------------$/
-		|| /^=============================/) {
+	    if (/$LOG_REVSEPARATOR/o || /$LOG_FILESEPARATOR/o) {
 		# End of a log entry.
 		my $revbranch;
 		($revbranch = $rev) =~ s/\.\d+$//;
-		print "$filename $rev Wanted: $revwanted "
-		    . "Revbranch: $revbranch Branch: $branch "
-		    . "Branchpoint: $branchpoint\n" if ($verbose);
+		print "$filename $rev Wanted: $revwanted ",
+		  "Revbranch: $revbranch Branch: $branch ",
+		    "Branchpoint: $branchpoint\n" if ($verbose);
 		if (!defined($revwanted) && defined($branch)
 		    && $branch eq $revbranch || !defined($tag)) {
 		    print "File revision $rev found for branch $branch\n"
@@ -1728,10 +1878,10 @@ again:
 		next;
 	    }
 	    else {
-		$log = $log . $_;
+		$log .= $_;
 	    }
 	}
-	if (/^===============/) {
+	if (/$LOG_FILESEPARATOR/o) {
 	    $state = "start";
 	    next;
 	}
@@ -1805,7 +1955,7 @@ sub readLog($;$) {
 # log info
 # ----------------------------
 	logentry:
-	while (!/^=========/) {
+	while (!/$LOG_FILESEPARATOR/o) {
 	    $_ = <$fh>;
 	    last logentry if (!defined($_));	# EOF
 	    print "R:", $_ if ($verbose);
@@ -1813,7 +1963,7 @@ sub readLog($;$) {
 		$rev = $1;
 		unshift(@allrevisions,$rev);
 	    }
-	    elsif (/^========/ || /^----------------------------$/) {
+	    elsif (/$LOG_FILESEPARATOR/o || /$LOG_REVSEPARATOR/o) {
 		next logentry;
 	    }
 	    else {
@@ -1847,7 +1997,7 @@ sub readLog($;$) {
 	    while (<$fh>) {
 		print "L:", $_ if ($verbose);
 		next line if (/^branches:\s/);
-		last line if (/^----------------------------$/ || /^=========/);
+		last line if (/$LOG_FILESEPARATOR/o || /$LOG_REVSEPARATOR/o);
 		$log{$rev} .= $_;
 	    }
 	    print "E:", $_ if ($verbose);
@@ -1866,11 +2016,10 @@ sub readLog($;$) {
 # This is not neccesary the same revision as marked as head in the RCS file.
 	my $headrev = $curbranch || "1";
 	($symrev{"MAIN"} = $headrev) =~ s/(\.?)(\d+)$/${1}0.$2/;
-	revision:
 	foreach $rev (@revorder) {
 	    if ($rev =~ /^(\S*)\.\d+$/ && $headrev eq $1) {
 		$symrev{"HEAD"} = $rev;
-		last revision;
+		last;
 	    }
 	}
 	($symrev{"HEAD"} = $headrev) =~ s/\.\d+$//
@@ -1888,7 +2037,7 @@ sub readLog($;$) {
 
 	foreach (reverse sort keys %symrev) {
 	    $rev = $symrev{$_};
-	    if ($rev =~ /^((.*)\.)?\b0\.(\d+)$/) {
+	    if ($rev =~ /^((.*)\.)0\.(\d+)$/) {
 		push(@branchnames, $_);
 		#
 		# A revision number of A.B.0.D really translates into
@@ -1901,21 +2050,22 @@ sub readLog($;$) {
 		# with the branch number 0.A, with the exception that
 		# it has no head to translate to if there is nothing on
 		# the branch, but I guess this can never happen?
-		# (the code below gracefully forgets about the branch
-		# if it should happen)
 		#
+		# Since some stupid people actually import/check in
+		# files with version 0.X we assume that the above cannot
+		# happen, and regard 0.X(.*) as a revision and not a branch.
+		#
 		$head = defined($2) ? $2 : "";
 		$branch = $3;
 		$branchrev = $head . ($head ne "" ? "." : "") . $branch;
 		my $regex;
-		($regex = $branchrev) =~ s/\./\\./g;
+		$regex = quotemeta $branchrev;
 		$rev = $head;
 
-		revision:
 		foreach my $r (@revorder) {
 		    if ($r =~ /^${regex}\b/) {
 			$rev = $branchrev;
-			last revision;
+			last;
 		    }
 		}
 		next if ($rev eq "");
@@ -1933,7 +2083,7 @@ sub readLog($;$) {
 	my ($onlyonbranch, $onlybranchpoint);
 	if ($onlyonbranch = $input{'only_with_tag'}) {
 	    $onlyonbranch = $symrev{$onlyonbranch};
-	    if ($onlyonbranch =~ s/\b0\.//) {
+	    if ($onlyonbranch =~ s/\.0\././) {
 		($onlybranchpoint = $onlyonbranch) =~ s/\.\d+$//;
 	    }
             else {
@@ -1971,6 +2121,20 @@ sub readLog($;$) {
 
 }
 
+sub printDiffLinks($$) {
+    my($text, $url) = @_;
+    my @extra;
+
+    local $_;
+    for ($DIFFTYPES{$defaultDiffType}{'colored'} ? qw(u) : qw(h)) {
+	my $f = $_ eq $defaultDiffType ? '' : $_;
+
+	push @extra, &link(lc $DIFFTYPES{$_}{'descr'}, "$url&f=$f");
+    }
+
+    print &link($text, $url), ' (', join(', ', @extra), ')';
+}
+
 sub printLog($;$) {
 	my ($link, $br, $brp);
 	($_,$link) = @_;
@@ -1999,29 +2163,40 @@ sub printLog($;$) {
 	    }
 	    print "\n Revision ";
 	    &download_link($fileurl, $_, $_,
-		$defaultViewable ? "text/x-cvsweb-markup" : undef);
+			   $defaultViewable ? "text/x-cvsweb-markup" : undef);
 	    if ($defaultViewable) {
-		print " / ";
-		&download_link($fileurl, $_, "(download)", $mimetype);
+		print " / (";
+		&download_link($fileurl, $_, "download", $mimetype);
+		print ")";
 	    }
 	    if (not $defaultTextPlain) {
-		print " / ";
-		&download_link($fileurl, $_, "(as text)",
-			   "text/plain");
+		print " / (";
+		&download_link($fileurl, $_, "as text", "text/plain");
+		print ")";
 	    }
 	    if (!$defaultViewable) {
-		print " / ";
-		&download_link($fileurl, $_, "(view)", "text/x-cvsweb-markup");
+		print " / (";
+		&download_link($fileurl, $_, "view", "text/x-cvsweb-markup");
+		print ")";
 	    }
 	    if ($allow_annotate) {
-		print " - ";
-		print "annotate";
+		print " - ";
+		print &link('annotate',
+			    sprintf('%s/%s?annotate=%s%s',
+				    $scriptname,
+				    urlencode($where),
+				    $_,
+				    $barequery));
 	    }
 	    # Plus a select link if enabled, and this version isn't selected
 	    if ($allow_version_select) {
 		if ((!defined($input{"r1"}) || $input{"r1"} ne $_)) {
-		    print " - [select for diffs]\n";
+		    print " - ";
+		    print &link('[select for diffs]',
+				sprintf('%s?r1=%s%s',
+					$scriptwhere,
+					$_,
+					$barequery));
 		}
 		else {
 		    print " - [selected]";
@@ -2036,13 +2211,13 @@ sub printLog($;$) {
 	}
 	if (defined @mytz) {
 	    my ($est) = $mytz[(localtime($date{$_}))[8]];
-	    print ", " . scalar localtime($date{$_}) . " $est (";
+	    print ", ", scalar localtime($date{$_}), " $est (";
 	} else {
-	    print ", " . scalar gmtime($date{$_}) . " UTC (";
+	    print ", ", scalar gmtime($date{$_}), " UTC (";
 	}
-	print readableTime(time() - $date{$_},1) . " ago)";
+	print readableTime(time() - $date{$_},1), " ago)";
 	print " by ";
-	print "" . $author{$_} . "\n";
+	print "", $author{$_}, "\n";
 	print "
Branch: ",$link?link_tags($revsym{$br}):$revsym{$br},"\n" if ($revsym{$br}); print "
CVS Tags: ",$link?link_tags($revsym{$_}):$revsym{$_},"" @@ -2076,23 +2251,28 @@ sub printLog($;$) { # Offer diff to previous revision if ($prev) { $diffrev{$prev} = 1; - print " to previous $prev\n"; - if (!$hr_default) { # offer a human readable version if not default - print "(colored)\n"; - } + + my $url = sprintf('%s.diff?r1=%s&r2=%s%s', + $scriptwhere, + $prev, + $_, + $barequery); + + print " to previous "; + printDiffLinks($prev, $url); } # # Plus, if it's on a branch, and it's not a vendor branch, # offer a diff with the branch point. if ($revsym{$brp} && !/^1\.1\.1\.\d+$/ && !defined($diffrev{$brp})) { - print " to branchpoint $brp\n"; - if (!$hr_default) { # offer a human readable version if not default - print "(colored)\n"; - } + my $url = sprintf('%s.diff?r1=%s&r2=%s%s', + $scriptwhere, + $brp, + $_, + $barequery); + + print " to branchpoint "; + printDiffLinks($brp, $url); } # # Plus, if it's on a branch, and it's not a vendor branch, @@ -2117,29 +2297,30 @@ sub printLog($;$) { } if (!defined($diffrev{$nextmain})) { $diffrev{$nextmain} = 1; - print " next main $nextmain\n"; - if (!$hr_default) { # offer a human readable version if not default - print "(colored)\n"; - } + + my $url = sprintf('%s.diff?r1=%s&r2=%s%s', + $scriptwhere, + $nextmain, + $_, + $barequery); + + print " next main "; + printDiffLinks($nextmain, $url); } } # Plus if user has selected only r1, then present a link # to make a diff to that revision if (defined($input{"r1"}) && !defined($diffrev{$input{"r1"}})) { $diffrev{$input{"r1"}} = 1; - print " to selected $input{'r1'}\n"; - if (!$hr_default) { # offer a human readable version if not default - print "(colored)\n"; - } + my $url = sprintf('%s.diff?r1=%s&r2=%s%s', + $scriptwhere, + $input{'r1'}, + $_, + $barequery); + + print " to selected "; + printDiffLinks($input{'r1'}, $url); } } print "
\n";
@@ -2158,11 +2339,10 @@ sub doLog($) {
         ($filename = $where) =~ s|^.*/||;
         $backurl = $scriptname . "/" . urlencode($upwhere) . $query;
 	print &link($backicon, "$backurl#$filename"),
-              " Up to ", &clickablePath($upwhere, 1), "

\n"; - print <Request diff between arbitrary revisions -


-EOF + " Up to ", &clickablePath($upwhere, 1), "

\n"; + print &link('Request diff between arbitrary revisions', '#diff'); + print '


'; + if ($curbranch) { print "Default branch: ", ($revsym{$curbranch} || $curbranch); } @@ -2190,7 +2370,7 @@ EOF print "

\n"; print "

\n"; foreach (@stickyvars) { - print "\n" + printf('', $_, $input{$_}) if (defined($input{$_}) && ((!defined($DEFAULTVALUE{$_}) || $input{$_} ne $DEFAULTVALUE{$_}) @@ -2220,8 +2400,8 @@ EOF print "
\n"; print ""; print "\n"; - print ""; - print ""; + print "\n"; if (@branchnames) { @@ -2256,13 +2436,9 @@ EOF print ""; - print ""; + print ""; print ""; print "\n"; print "
Preferred Diff type:"; + print "
Preferred Diff type:"; printDiffSelect($use_java_script); print "
"; print "\n"; print "Sort log by:"; + printLogSortSelect($use_java_script); + print "
"; @@ -2332,17 +2508,16 @@ sub human_readable_diff($){ $date2 = $r2d; } - print "

Diff for /$where_nd between version $rev1 and $rev2

\n"; - - print "\n"; - print "\n"; - print "
"; - print "version $rev1"; + print "

Diff for /$where_nd between version $rev1 and $rev2

\n", + "\n", + "\n", + "\n"; - print "\n", + "\n"; @@ -2467,12 +2642,12 @@ sub navigateHeader($$$$$) { $swhere = urlencode($filename) if ($swhere eq ""); print "<\!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">"; print "\n\n"; - print ''; + print ''; print "\n$path$filename - $title - $rev\n"; - print "\n"; + print "$body_tag_for_src\n"; print "
", + "version $rev1"; print ", $date1" if (defined($date1)); print "
Tag: $sym1\n" if ($sym1); - print "
"; - print "version $rev2"; + print "", + "version $rev2"; print ", $date2" if (defined($date2)); print "
Tag: $sym2\n" if ($sym1); print "
"; print ""; @@ -2483,10 +2658,10 @@ sub navigateHeader($$$$$) { sub plural_write($$) { my ($num,$text) = @_; if ($num != 1) { - $text = $text . "s"; + $text .= "s"; } if ($num > 0) { - return $num . " " . $text; + return join(' ', $num, $text); } else { return ""; @@ -2530,7 +2705,7 @@ sub readableTime($$) { my $resttime = plural_write(int ($rest / $break), $desc{$break}); if ($resttime) { - $retval = $retval . ", " . $resttime; + $retval .= ", $resttime"; } } @@ -2554,24 +2729,26 @@ sub clickablePath($$) { $retval = "[$cvstree]"; } else { - $retval = $retval . " [$cvstree]"; + $retval .= ' ' . &link("[$cvstree]", sprintf('%s/%s#dirlist', + $scriptname, + $query)); my $wherepath = ''; my ($lastslash) = $pathname =~ m|/$|; foreach (split(/\//, $pathname)) { - $retval = $retval . " / "; - $wherepath = $wherepath . '/' . $_; + $retval .= " / "; + $wherepath .= "/$_"; my ($last) = "$wherepath/" eq "/$pathname" || $wherepath eq "/$pathname"; if ($clickLast || !$last) { - $retval = $retval . "$_"; + $retval .= &link($_, join('', + $scriptname, + urlencode($wherepath), + (!$last || $lastslash ? '/' : ''), + $query, + (!$last || $lastslash ? "#dirlist" : ""))); } else { # do not make a link to the current dir - $retval = $retval . $_; + $retval .= $_; } } } @@ -2603,8 +2780,7 @@ sub chooseCVSRoot() { foreach $k (@foo) { print "\n"; + print ">", ($CVSROOTdescr{$k} ? $CVSROOTdescr{$k} : $k), "\n"; } print "\n"; print ""; @@ -2628,7 +2804,7 @@ sub chooseMirror() { print "\nThis cvsweb is mirrored in:\n"; foreach $mirror (keys %MIRRORS) { print ", " if ($moremirrors); - print qq($mirror\n); + print &link(htmlquote($mirror),$MIRRORS{$mirror}); $moremirrors = 1; } print "

\n"; @@ -2669,34 +2845,30 @@ sub fileSortCmp() { sub download_url($$;$) { my ($url,$revision,$mimetype) = @_; - $revision =~ s/\b0\.//; + $revision =~ s/\.0\././; - if (defined($checkout_magic) + if (defined($checkoutMagic) && (!defined($mimetype) || $mimetype ne "text/x-cvsweb-markup")) { - my ($path); - ($path = $where) =~ s|/[^/]*$|/|; + my $path = $where; + $path =~ s|/[^/]*$|/|; $url = "$scriptname/$checkoutMagic/${path}$url"; } $url .= "?rev=$revision"; - $url .= "&content-type=$mimetype" if (defined($mimetype)); + $url .= '&content-type=' . urlencode($mimetype) if (defined($mimetype)); - return $url; + $url; } # Presents a link to download the # selected revision sub download_link($$$;$) { - my ($url,$revision,$textlink,$mimetype) = @_; - my ($fullurl) = download_url($url,$revision,$mimetype); - my ($paren) = $textlink =~ /^\(/; - $textlink =~ s/^\(// if ($paren); - $textlink =~ s/\)$// if ($paren); - print "(" if ($paren); - print "$textlink"; - print ")" if ($paren); } # Returns a Query string with the @@ -2747,7 +2924,7 @@ sub toggleQuery($$) { my ($value) = defined($vars{$var}) ? $vars{$var} : ""; my ($default) = defined($DEFAULTVALUE{$var}) ? $DEFAULTVALUE{$var} : ""; if ($value ne $default) { - $newquery .= "&" if ($newquery ne ""); + $newquery .= "&" if ($newquery ne ""); $newquery .= urlencode($var) . "=" . urlencode($value); } } @@ -2758,12 +2935,38 @@ sub toggleQuery($$) { } sub urlencode($) { - my ($in) = @_; - my ($out); - ($out = $in) =~ s/([\000-+{-\377])/sprintf("%%%02x", ord($1))/ge; - return $out; + local($_) = @_; + + s/[\000-+{-\377]/sprintf("%%%02x", ord($&))/ge; + + + $_; } +sub htmlquote($) { + local($_) = @_; + + # Special Characters; RFC 1866 + s/&/&/g; + s/\"/"/g; + s//>/g; + + $_; +} + +sub htmlunquote($) { + local($_) = @_; + + # Special Characters; RFC 1866 + s/"/\"/g; + s/<//g; + s/&/&/g; + + $_; +} + sub http_header(;$) { my $content_type = shift || "text/html"; if (defined($moddate)) { @@ -2771,7 +2974,7 @@ sub http_header(;$) { Apache->request->header_out("Last-Modified" => scalar gmtime($moddate) . " GMT"); } else { - print "Last-Modified: " . scalar gmtime($moddate) . " GMT\r\n"; + print "Last-Modified: ", scalar gmtime($moddate), " GMT\r\n"; } } if ($is_mod_perl) { @@ -2798,7 +3001,7 @@ sub http_header(;$) { } select(GZIP); $gzip_open = 1; -# print "" if ($content_type eq "text/html"); +# print "" if ($content_type =~ m|^text/html\b|); } else { if ($is_mod_perl) { @@ -2822,12 +3025,13 @@ sub http_header(;$) { sub html_header($) { my ($title) = @_; - my $version = '$zRevision: 1.101 $ $kRevision: 1.24 $'; #' - http_header(); + my $version = '$zRevision: 1.104 $ $kRevision: 1.41 $'; #' + http_header($charset ne "" ? "text/html; charset=$charset" : "text/html"); print < + $title @@ -2850,10 +3054,9 @@ sub link_tags($) { foreach my $sym (split(", ", $tags)) { $ret .= ",\n" if ($ret ne ""); - $ret .= "$sym"; + $ret .= &link($sym, $fileurl . toggleQuery('only_with_tag',$sym)); } - return $ret."\n"; + return "$ret\n"; } # @@ -2867,6 +3070,17 @@ sub forbidden_module($) { } return 0; +} + +# Close the GZIP handle remove the tie. + +sub gzipclose { + if ($gzip_open) { + select(STDOUT); + close(GZIP); + untie *GZIP; + $gzip_open = 0; + } } # implement a gzipped file handle via the Compress:Zlib compression

"; - print "$backicon"; + print &link($backicon, "$swhere$query#rev$rev"); print " Return to ", &link("$filename","$swhere$query#rev$rev")," CVS log"; print " $fileicon