[BACK]Return to cvsweb.cgi CVS log [TXT][DIR] Up to [cvsweb.bsd.lv] / cvsweb

Diff for /cvsweb/cvsweb.cgi between version 1.1.1.22 and 3.18

version 1.1.1.22, 2001/01/14 08:49:27 version 3.18, 2000/09/04 14:50:22
Line 1 
Line 1 
 #!/usr/bin/perl -wT  #!/usr/bin/perl5 -ws
 #  #
 # cvsweb - a CGI interface to CVS trees.  # cvsweb - a CGI interface to CVS trees.
 #  #
Line 9 
Line 9 
 #             Ken Coar         <coar@Apache.Org>  #             Ken Coar         <coar@Apache.Org>
 #             Dick Balaska     <dick@buckosoft.com>  #             Dick Balaska     <dick@buckosoft.com>
 #             Akinori MUSHA    <knu@FreeBSD.org>  #             Akinori MUSHA    <knu@FreeBSD.org>
 #             Jens-Uwe Mager   <jum@helios.de>  
 #  #
 # Based on:  # Based on:
 # * Bill Fenners cvsweb.cgi revision 1.28 available from:  # * Bill Fenners cvsweb.cgi revision 1.28 available from:
Line 18 
Line 17 
 # Copyright (c) 1996-1998 Bill Fenner  # Copyright (c) 1996-1998 Bill Fenner
 #           (c) 1998-1999 Henner Zeller  #           (c) 1998-1999 Henner Zeller
 #           (c) 1999      Henrik Nordstrom  #           (c) 1999      Henrik Nordstrom
 #           (c) 2000-2001 Akinori MUSHA  #           (c) 2000      Akinori MUSHA
 # All rights reserved.  # All rights reserved.
 #  #
 # Redistribution and use in source and binary forms, with or without  # Redistribution and use in source and binary forms, with or without
Line 42 
Line 41 
 # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF  # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 # SUCH DAMAGE.  # SUCH DAMAGE.
 #  #
 # $zId: cvsweb.cgi,v 1.105 2000/11/26 01:51:04 jumager Exp $  # $zId: cvsweb.cgi,v 1.94 2000/08/24 06:41:22 hnordstrom Exp $
 # $Idaemons: /home/cvs/cvsweb/cvsweb.cgi,v 1.64 2001/01/13 07:48:09 knu Exp $  # $Id$
 #  #
 ###  ###
   
 require 5.000;  
   
 use strict;  use strict;
   
 use vars qw (  use vars qw (
     $cvsweb_revision      $config $allow_version_select $verbose
     $mydir $uname $config $allow_version_select $verbose      %CVSROOT %CVSROOTdescr %MIRRORS %DEFAULTVALUE %ICONS %MTYPES
     @CVSrepositories @CVSROOT %CVSROOT %CVSROOTdescr  
     %MIRRORS %DEFAULTVALUE %ICONS %MTYPES  
     @DIFFTYPES %DIFFTYPES @LOGSORTKEYS %LOGSORTKEYS  
     %alltags @tabcolors %fileinfo %tags @branchnames %nameprinted      %alltags @tabcolors %fileinfo %tags @branchnames %nameprinted
     %symrev %revsym @allrevisions %date %author @revdisplayorder      %symrev %revsym @allrevisions %date %author @revdisplayorder
     @revisions %state %difflines %log %branchpoint @revorder      @revisions %state %difflines %log %branchpoint @revorder
     $prcgi @prcategories $re_prcategories $prkeyword $re_prkeyword $mancgi      $prcgi @prcategories $prcategories $mancgi
     $checkoutMagic $doCheckout $scriptname $scriptwhere      $checkoutMagic $doCheckout $scriptname $scriptwhere
     $where $pathinfo $Browser $nofilelinks $maycompress @stickyvars      $where $pathinfo $Browser $nofilelinks $maycompress @stickyvars
     %funcline_regexp $is_mod_perl      %funcline_regexp $is_mod_perl
     $is_links $is_lynx $is_w3m $is_msie $is_mozilla3 $is_textbased      $is_lynx $is_w3m $is_msie $is_mozilla3 $is_textbased
     %input $query $barequery $sortby $bydate $byrev $byauthor      %input $query $barequery $sortby $bydate $byrev $byauthor
     $bylog $byfile $defaultDiffType $logsort $cvstree $cvsroot      $bylog $byfile $hr_default $logsort $cvstree $cvsroot
     $mimetype $charset $defaultTextPlain $defaultViewable      $mimetype $defaultTextPlain $defaultViewable $allow_compress
     $command_path %CMD $allow_compress      $GZIPBIN $backicon $diricon $fileicon $fullname $newname
     $backicon $diricon $fileicon      $cvstreedefault $body_tag $logo $defaulttitle $address
     $fullname $newname $cvstreedefault      $backcolor $long_intro $short_instruction $shortLogLen
     $body_tag $body_tag_for_src $logo $defaulttitle $address  
     $long_intro $short_instruction $shortLogLen  
     $show_author $dirtable $tablepadding $columnHeaderColorDefault      $show_author $dirtable $tablepadding $columnHeaderColorDefault
     $columnHeaderColorSorted $hr_breakable $showfunc $hr_ignwhite      $columnHeaderColorSorted $hr_breakable $showfunc $hr_ignwhite
     $hr_ignkeysubst $diffcolorHeading $diffcolorEmpty $diffcolorRemove      $hr_ignkeysubst $diffcolorHeading $diffcolorEmpty $diffcolorRemove
Line 80  use vars qw (
Line 72  use vars qw (
     $difffontsize $inputTextSize $mime_types $allow_annotate      $difffontsize $inputTextSize $mime_types $allow_annotate
     $allow_markup $use_java_script $open_extern_window      $allow_markup $use_java_script $open_extern_window
     $extern_window_width $extern_window_height $edit_option_form      $extern_window_width $extern_window_height $edit_option_form
     $show_subdir_lastmod $show_log_in_markup $preformat_in_markup $v      $checkout_magic $show_subdir_lastmod $show_log_in_markup $v
     $navigationHeaderColor $tableBorderColor $markupLogColor      $navigationHeaderColor $tableBorderColor $markupLogColor
     $tabstop $state $annTable $sel $curbranch @HideModules      $tabstop $state $annTable $sel $curbranch @HideModules
     $module $use_descriptions %descriptions @mytz $dwhere $moddate      $module $use_descriptions %descriptions @mytz $dwhere $moddate
     $use_moddate $has_zlib $gzip_open      $use_moddate
     $allow_tar @tar_options @gzip_options @cvs_options  
     $LOG_FILESEPARATOR $LOG_REVSEPARATOR  
 );  );
   
 sub printDiffSelect($);  sub printDiffSelect($);
 sub printDiffLinks($$);  
 sub printLogSortSelect($);  
 sub findLastModifiedSubdirs(@);  sub findLastModifiedSubdirs(@);
 sub htmlify_sub(&$);  
 sub htmlify($;$);  sub htmlify($;$);
 sub spacedHtmlText($;$);  sub spacedHtmlText($);
 sub link($$);  sub link($$);
 sub revcmp($$);  sub revcmp($$);
 sub fatal($$);  sub fatal($$);
 sub redirect($);  sub redirect($);
 sub safeglob($);  sub safeglob($);
 sub search_path($);  
 sub getMimeTypeFromSuffix($);  sub getMimeTypeFromSuffix($);
 sub head($;$);  
 sub scan_directives(@);  
 sub doAnnotate($$);  sub doAnnotate($$);
 sub doCheckout($$);  sub doCheckout($$);
 sub cvswebMarkup($$$);  sub cvswebMarkup($$$);
Line 127  sub download_url($$;$);
Line 111  sub download_url($$;$);
 sub download_link($$$;$);  sub download_link($$$;$);
 sub toggleQuery($$);  sub toggleQuery($$);
 sub urlencode($);  sub urlencode($);
 sub htmlquote($);  
 sub htmlunquote($);  
 sub hrefquote($);  
 sub http_header(;$);  sub http_header(;$);
 sub html_header($);  sub html_header($);
 sub html_footer();  sub html_footer();
Line 137  sub link_tags($);
Line 118  sub link_tags($);
 sub forbidden_module($);  sub forbidden_module($);
   
 ##### Start of Configuration Area ########  ##### Start of Configuration Area ########
 delete $ENV{PATH};  use Cwd;
   
 $cvsweb_revision = join('.', '1.105', (split(/ /,  
  q$Idaemons: /home/cvs/cvsweb/cvsweb.cgi,v 1.64 2001/01/13 07:48:09 knu Exp $  
 ))[2]);  
   
 use File::Basename;  
   
 ($mydir) = (dirname($0) =~ /(.*)/); # untaint  
   
 # == EDIT this ==  # == EDIT this ==
 # Locations to search for user configuration, in order:  # User configuration is stored in
 for (  $config = undef;
      "$mydir/cvsweb.conf",  
      '/usr/local/etc/cvsweb/cvsweb.conf'  for ($ENV{CVSWEB_CONFIG},
     ) {  #     '/home/knu/etc/cvsweb.conf',
     if (defined($_) && -r $_) {       '/usr/local/etc/cvsweb.conf',
         $config = $_;       getcwd . '/cvsweb.conf') {
         last;    $config = $_ if defined($_) && -r $_;
     }  
 }  }
   
 # == Configuration defaults ==  # == Configuration defaults ==
Line 168  $allow_version_select = 1;
Line 140  $allow_version_select = 1;
   
 ######## Configuration variables #########  ######## Configuration variables #########
 # These are defined to allow checking with perl -cw  # These are defined to allow checking with perl -cw
 @CVSrepositories = @CVSROOT = %CVSROOT =  %CVSROOT = %MIRRORS = %DEFAULTVALUE = %ICONS = %MTYPES =
 %MIRRORS = %DEFAULTVALUE = %ICONS = %MTYPES =  
 %tags = %alltags = @tabcolors = ();  %tags = %alltags = @tabcolors = ();
 $cvstreedefault = $body_tag = $body_tag_for_src =  $cvstreedefault = $body_tag = $logo = $defaulttitle = $address =
 $logo = $defaulttitle = $address =  $backcolor = $long_intro = $short_instruction = $shortLogLen =
 $long_intro = $short_instruction = $shortLogLen =  
 $show_author = $dirtable = $tablepadding = $columnHeaderColorDefault =  $show_author = $dirtable = $tablepadding = $columnHeaderColorDefault =
 $columnHeaderColorSorted = $hr_breakable = $showfunc = $hr_ignwhite =  $columnHeaderColorSorted = $hr_breakable = $showfunc = $hr_ignwhite =
 $hr_ignkeysubst = $diffcolorHeading = $diffcolorEmpty = $diffcolorRemove =  $hr_ignkeysubst = $diffcolorHeading = $diffcolorEmpty = $diffcolorRemove =
Line 181  $diffcolorChange = $diffcolorAdd = $diffcolorDarkChang
Line 151  $diffcolorChange = $diffcolorAdd = $diffcolorDarkChang
 $difffontsize = $inputTextSize = $mime_types = $allow_annotate =  $difffontsize = $inputTextSize = $mime_types = $allow_annotate =
 $allow_markup = $use_java_script = $open_extern_window =  $allow_markup = $use_java_script = $open_extern_window =
 $extern_window_width = $extern_window_height = $edit_option_form =  $extern_window_width = $extern_window_height = $edit_option_form =
 $show_subdir_lastmod = $show_log_in_markup = $v =  $checkout_magic = $show_subdir_lastmod = $show_log_in_markup = $v =
 $navigationHeaderColor = $tableBorderColor = $markupLogColor =  $navigationHeaderColor = $tableBorderColor = $markupLogColor =
 $tabstop = $use_moddate = $moddate = $gzip_open = undef;  $tabstop = $use_moddate = $moddate = 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 #####  ##### End of configuration variables #####
   
 use Time::Local;  use Time::Local;
 use IPC::Open2;  use IPC::Open2;
   
 # Check if the zlib C library interface is installed, and if yes  
 # we can avoid using the extra gzip process.  
 eval {  
         require Compress::Zlib;  
 };  
 $has_zlib = !$@;  
   
 $verbose = $v;  $verbose = $v;
 $checkoutMagic = "~checkout~";  $checkoutMagic = "~checkout~";
 $pathinfo = defined($ENV{PATH_INFO}) ? $ENV{PATH_INFO} : '';  $pathinfo = defined($ENV{PATH_INFO}) ? $ENV{PATH_INFO} : '';
 $where = $pathinfo;  $where = $pathinfo;
 $doCheckout = ($where =~ m|^/$checkoutMagic/|);  $doCheckout = ($where =~ /^\/$checkoutMagic/);
 $where =~ s|^/$checkoutMagic/|/|;  $where =~ s|^/($checkoutMagic)?||;
 $where =~ s|^/||;  $where =~ s|/+$||;
 $scriptname = defined($ENV{SCRIPT_NAME}) ? $ENV{SCRIPT_NAME} : '';  $scriptname = defined($ENV{SCRIPT_NAME}) ? $ENV{SCRIPT_NAME} : '';
 $scriptname =~ s|^/*|/|;  $scriptname =~ s|^/?|/|;
   $scriptname =~ s|/+$||;
 # Let's workaround thttpd's stupidity..  $scriptwhere = $scriptname;
 if ($scriptname =~ m|/$|) {  if ($where) {
     $pathinfo .= '/';      $scriptwhere .= '/' . urlencode($where);
     my $re = quotemeta $pathinfo;  
     $scriptname =~ s/$re$//;  
 }  }
   
 $scriptwhere = $scriptname;  
 $scriptwhere .= '/' . urlencode($where);  
 $where = '/' if ($where eq '');  
   
 $is_mod_perl = defined($ENV{MOD_PERL});  $is_mod_perl = defined($ENV{MOD_PERL});
   
 # in lynx, it it very annoying to have two links  # in lynx, it it very annoying to have two links
 # per file, so disable the link at the icon  # per file, so disable the link at the icon
 # in this case:  # in this case:
 $Browser = $ENV{HTTP_USER_AGENT} || '';  $Browser = $ENV{HTTP_USER_AGENT};
 $is_links = ($Browser =~ m`^Links `);  
 $is_lynx = ($Browser =~ m`^Lynx/`i);  $is_lynx = ($Browser =~ m`^Lynx/`i);
 $is_w3m = ($Browser =~ m`^w3m/`i);  $is_w3m = ($Browser =~ m`^w3m/`i);
 $is_msie = ($Browser =~ m`MSIE`);  $is_msie = ($Browser =~ m`MSIE`);
 $is_mozilla3 = ($Browser =~ m`^Mozilla/[3-9]`);  $is_mozilla3 = ($Browser =~ m`^Mozilla/[3-9]`);
   
 $is_textbased = ($is_links || $is_lynx || $is_w3m);  $is_textbased = ($is_lynx || $is_w3m);
   
 $nofilelinks = $is_textbased;  $nofilelinks = $is_textbased;
   
Line 287  $nofilelinks = $is_textbased;
Line 197  $nofilelinks = $is_textbased;
 #  braindamaged MS-Internet Exploders claim that they  #  braindamaged MS-Internet Exploders claim that they
 # accept gzip .. but don't in fact and  # accept gzip .. but don't in fact and
 # display garbage then :-/  # display garbage then :-/
 # Turn off gzip if running under mod_perl and no zlib is available,  # Turn off gzip if running under mod_perl. piping does
 # piping does not work as expected inside the server.  # not work as expected inside the server. One can probably
 $maycompress = (((defined($ENV{HTTP_ACCEPT_ENCODING})  # achieve the same result using Apache::GZIPFilter.
                  && $ENV{HTTP_ACCEPT_ENCODING} =~ m`gzip`)  $maycompress = (($ENV{HTTP_ACCEPT_ENCODING} =~ m`gzip`
                  || $is_mozilla3)                   || $is_mozilla3)
                 && !$is_msie                  && !$is_msie
                 && !($is_mod_perl && !$has_zlib));                  && !$is_mod_perl);
   
 # put here the variables we need in order  # put here the variables we need in order
 # to hold our state - they will be added (with  # to hold our state - they will be added (with
Line 302  $maycompress = (((defined($ENV{HTTP_ACCEPT_ENCODING})
Line 212  $maycompress = (((defined($ENV{HTTP_ACCEPT_ENCODING})
 @stickyvars = qw(cvsroot hideattic sortby logsort f only_with_tag);  @stickyvars = qw(cvsroot hideattic sortby logsort f only_with_tag);
   
 if (-f $config) {  if (-f $config) {
    require $config      do $config;
      || &fatal("500 Internal Error",  }
                sprintf('Error in loading configuration file: %s<BR><BR>%s<BR>',  else {
                        $config, &htmlify($@)));  
 } else {  
    &fatal("500 Internal Error",     &fatal("500 Internal Error",
           'Configuration not found.  Set the variable <code>$config</code> '            'Configuration not found.  Set the variable <code>$config</code> '
           . 'in cvsweb.cgi, or the environment variable '            . 'in cvsweb.cgi, or the environment variable '
Line 319  $query = $ENV{QUERY_STRING};
Line 227  $query = $ENV{QUERY_STRING};
   
 if (defined($query) && $query ne '') {  if (defined($query) && $query ne '') {
     foreach (split(/&/, $query)) {      foreach (split(/&/, $query)) {
         y/+/ /;  
         s/%(..)/sprintf("%c", hex($1))/ge;      # unquote %-quoted          s/%(..)/sprintf("%c", hex($1))/ge;      # unquote %-quoted
         if (/(\S+)=(.*)/) {          if (/(\S+)=(.*)/) {
             $input{$1} = $2 if ($2 ne "");              $input{$1} = $2 if ($2 ne "");
Line 359  foreach (keys %DEFAULTVALUE)
Line 266  foreach (keys %DEFAULTVALUE)
 }  }
   
 $barequery = "";  $barequery = "";
 my @barequery;  
 foreach (@stickyvars) {  foreach (@stickyvars) {
     # construct a query string with the sticky non default parameters set      # construct a query string with the sticky non default parameters set
     if (defined($input{$_}) && $input{$_} ne '' &&      if (defined($input{$_}) && $input{$_} ne '' &&
         !(defined($DEFAULTVALUE{$_}) && $input{$_} eq $DEFAULTVALUE{$_})) {          !(defined($DEFAULTVALUE{$_}) && $input{$_} eq $DEFAULTVALUE{$_})) {
         push @barequery, join('=', urlencode($_), urlencode($input{$_}));          if ($barequery) {
               $barequery = $barequery . "&amp;";
           }
           my $thisval = urlencode($_) . "=" . urlencode($input{$_});
           $barequery .= $thisval;
     }      }
 }  }
 # is there any query ?  # is there any query ?
 if (@barequery) {  if ($barequery) {
     $barequery = join('&', @barequery);  
     $query = "?$barequery";      $query = "?$barequery";
     $barequery = "&$barequery";      $barequery = "&amp;" . $barequery;
 }  }
 else {  else {
     $query = "";      $query = "";
 }  }
 undef @barequery;  
   
 if (defined($input{path})) {  
     redirect("$scriptname/$input{path}$query");  
 }  
   
 # get actual parameters  # get actual parameters
 $sortby = $input{"sortby"};  $sortby = $input{"sortby"};
 $bydate = 0;  $bydate = 0;
Line 405  else {
Line 309  else {
     $byfile = 1;      $byfile = 1;
 }  }
   
 $defaultDiffType = $input{'f'};  $hr_default = $input{'f'} eq 'h';
   
 $logsort = $input{'logsort'};  $logsort = $input{'logsort'};
   
 my @tmp = @CVSrepositories;  
 my @pair;  
   
 while (@pair = splice(@tmp, 0, 2)) {  
     my($key, $val) = @pair;  
     my($descr, $cvsroot) = @$val;  
   
     next if !-d $cvsroot;  
   
     $CVSROOTdescr{$key} = $descr;  
     $CVSROOT{$key} = $cvsroot;  
     push @CVSROOT, $key;  
 }  
 undef @tmp;  
 undef @pair;  
   
 ## Default CVS-Tree  ## Default CVS-Tree
 if (!defined($CVSROOT{$cvstreedefault})) {  if (!defined($CVSROOT{$cvstreedefault})) {
    &fatal("500 Internal Error",     &fatal("500 Internal Error",
Line 443  if ($input{'cvsroot'} && $CVSROOT{$input{'cvsroot'}}) 
Line 332  if ($input{'cvsroot'} && $CVSROOT{$input{'cvsroot'}}) 
 $cvsroot = $CVSROOT{$cvstree};  $cvsroot = $CVSROOT{$cvstree};
   
 # create icons out of description  # create icons out of description
 my $k;  foreach my $k (keys %ICONS) {
 foreach $k (keys %ICONS) {  
     no strict 'refs';      no strict 'refs';
     my ($itxt,$ipath,$iwidth,$iheight) = @{$ICONS{$k}};      my ($itxt,$ipath,$iwidth,$iheight) = @{$ICONS{$k}};
     if ($ipath) {      if ($ipath) {
         ${"${k}icon"} = sprintf('<IMG SRC="%s" ALT="%s" BORDER="0" WIDTH="%d" HEIGHT="%d">',          ${"${k}icon"} = "<IMG SRC=\"$ipath\" ALT=\"$itxt\" BORDER=\"0\" WIDTH=\"$iwidth\" HEIGHT=\"$iheight\">";
                                 hrefquote($ipath), htmlquote($itxt), $iwidth, $iheight)  
     }      }
     else {      else {
         ${"${k}icon"} = $itxt;          ${"${k}icon"} = $itxt;
     }      }
 }  }
 undef $k;  
   
 my $config_cvstree = "$config-$cvstree";  
   
 # Do some special configuration for cvstrees  # Do some special configuration for cvstrees
 if (-f $config_cvstree) {  do "$config-$cvstree" if (-f "$config-$cvstree");
    require $config_cvstree  
      || &fatal("500 Internal Error",  
                sprintf('Error in loading configuration file: %s<BR><BR>%s<BR>',  
                        $config_cvstree, &htmlify($@)));  
 }  
 undef $config_cvstree;  
   
 $re_prcategories = '(?:' . join('|', @prcategories) . ')' if @prcategories;  $prcategories = '(?:' . join('|', @prcategories) . ')';
 $re_prkeyword = quotemeta($prkeyword) if defined($prkeyword);  
 $prcgi .= '%s' if defined($prcgi) && $prcgi !~ /%s/;  $prcgi .= '%s' if defined($prcgi) && $prcgi !~ /%s/;
   
 $fullname = "$cvsroot/$where";  $fullname = $cvsroot . '/' . $where;
 $mimetype = &getMimeTypeFromSuffix ($fullname);  $mimetype = &getMimeTypeFromSuffix ($fullname);
 $defaultTextPlain = ($mimetype eq "text/plain");  $defaultTextPlain = ($mimetype eq "text/plain");
 $defaultViewable = $allow_markup && viewable($mimetype);  $defaultViewable = $allow_markup && viewable($mimetype);
   
 my $rewrite = 0;  # search for GZIP if compression allowed
   # We've to find out if the GZIP-binary exists .. otherwise
 if ($pathinfo =~ m|//|) {  # ge get an Internal Server Error if we try to pipe the
     $pathinfo =~ y|/|/|s;  # output through the nonexistent gzip ..
     $rewrite = 1;  # any more elegant ways to prevent this are welcome!
   if ($allow_compress && $maycompress) {
       foreach (split(/:/, $ENV{PATH})) {
           if (-x "$_/gzip") {
               $GZIPBIN = "$_/gzip";
               last;
           }
       }
 }  }
   
 if (-d $fullname && $pathinfo !~ m|/$|) {  if (-d $fullname) {
     $pathinfo .= '/';      #
     $rewrite = 1;      # ensure, that directories always end with (exactly) one '/'
       # to allow relative URL's. If they're not, make a redirect.
       ##
       if (!($pathinfo =~ m|/$|) || ($pathinfo =~ m |/{2,}$|)) {
           redirect ($scriptwhere . '/' . $query);
       }
       else {
           $where .= '/';
           $scriptwhere .= '/';
       }
 }  }
   
 if (!-d $fullname && $pathinfo =~ m|/$|) {  
     chop $pathinfo;  
     $rewrite = 1;  
 }  
   
 if ($rewrite) {  
     redirect($scriptname . urlencode($pathinfo) . $query);  
 }  
   
 undef $rewrite;  
   
 if (!-d $cvsroot) {  if (!-d $cvsroot) {
     &fatal("500 Internal Error",'$CVSROOT not found!<P>The server on which the CVS tree lives is probably down.  Please try again in a few minutes.');      &fatal("500 Internal Error",'$CVSROOT not found!<P>The server on which the CVS tree lives is probably down.  Please try again in a few minutes.');
 }  }
Line 512  $module = $1;
Line 394  $module = $1;
 if ($module && &forbidden_module($module)) {  if ($module && &forbidden_module($module)) {
     &fatal("403 Forbidden", "Access to $where forbidden.");      &fatal("403 Forbidden", "Access to $where forbidden.");
 }  }
   
 #  
 # Handle tarball downloads before any headers are output.  
 #  
 if ($input{tarball}) {  
     &fatal("403 Forbidden", "Downloading tarballs is prohibited.")  
       unless $allow_tar;  
     my($module) = ($where =~ m,^/?(.*),);       # untaint  
     $module =~ s,/[^/]*$,,;  
     my($basedir) = ($module =~ m,([^/]+)$,);  
   
     if ($basedir eq '' || $module eq '') {  
         &fatal("500 Internal Error", "You cannot download the top level directory.");  
     }  
   
     my $tmpdir = "/tmp/.cvsweb.$$." . int(time);  
   
     mkdir($tmpdir, 0700)  
       or &fatal("500 Internal Error", "Unable to make temporary directory: $!");  
   
     my $fatal = '';  
   
     while (1) {  
         my $tag = (exists $input{only_with_tag} && length $input{only_with_tag})  
           ? $input{only_with_tag} : "HEAD";  
   
         system $CMD{cvs}, @cvs_options, '-Qd', $cvsroot, 'export', '-r', $tag, '-d', "$tmpdir/$basedir", $module  
           and $fatal = "500 Internal Error","cvs co failure: $!: $module"  
             && last;  
   
         $| = 1; # Essential to get the buffering right.  
   
         print "Content-type: application/x-gzip\r\n\r\n";  
   
         system "$CMD{tar} @tar_options -cf - -C $tmpdir $basedir | $CMD{gzip} @gzip_options -c"  
           and $fatal = "500 Internal Error","tar zc failure: $!: $basedir"  
             && last;  
   
         last;  
     }  
   
     system $CMD{rm}, '-rf', $tmpdir if -d $tmpdir;  
   
     &fatal($fatal) if $fatal;  
   
     exit;  
 }  
   
 ##############################  ##############################
 # View a directory  # View a directory
 ###############################  ###############################
 if (-d $fullname) {  elsif (-d $fullname) {
         my $dh = do {local(*DH);};          my $dh = do {local(*DH);};
         opendir($dh, $fullname) || &fatal("404 Not Found","$where: $!");          opendir($dh, $fullname) || &fatal("404 Not Found","$where: $!");
         my @dir = readdir($dh);          my @dir = readdir($dh);
Line 619  if (-d $fullname) {
Line 453  if (-d $fullname) {
             }              }
             print "<table  width=\"100%\" border=0 cellspacing=1 cellpadding=$tablepadding>\n";              print "<table  width=\"100%\" border=0 cellspacing=1 cellpadding=$tablepadding>\n";
             $infocols++;              $infocols++;
             printf '<tr><th align=left bgcolor="%s">',              print "<tr><th align=left bgcolor=\"" . (($byfile) ?
               $byfile ? $columnHeaderColorSorted : $columnHeaderColorDefault;                                                     $columnHeaderColorSorted :
             if ($byfile) {                                                     $columnHeaderColorDefault) . "\">";
                 print 'File';              print "<a href=\"./" . &toggleQuery("sortby","file") .
             } else {                  "#dirlist\">" if (!$byfile);
                 print &link('File', sprintf('./%s#dirlist',              print "File";
                                             &toggleQuery("sortby", "file")));              print "</a>" if (!$byfile);
             }  
             print "</th>";              print "</th>";
             # do not display the other column-headers, if we do not have any files              # do not display the other column-headers, if we do not have any files
             # with revision information:              # with revision information:
             if (scalar(%fileinfo)) {              if (scalar(%fileinfo)) {
                 $infocols++;                  $infocols++;
                 printf '<th align=left bgcolor="%s">',                  print "<th align=left bgcolor=\"" . (($byrev) ?
                   $byrev ? $columnHeaderColorSorted : $columnHeaderColorDefault;                                                     $columnHeaderColorSorted :
                 if ($byrev) {                                                     $columnHeaderColorDefault) . "\">";
                     print 'Rev.';                  print "<a href=\"./" . &toggleQuery ("sortby","rev") .
                 } else {                      "#dirlist\">" if (!$byrev);
                     print &link('Rev.', sprintf('./%s#dirlist',                  print "Rev.";
                                                 &toggleQuery("sortby", "rev")));                  print "</a>" if (!$byrev);
                 }  
                 print "</th>";                  print "</th>";
                 $infocols++;                  $infocols++;
                 printf '<th align=left bgcolor="%s">',                  print "<th align=left bgcolor=\"" . (($bydate) ?
                   $bydate ? $columnHeaderColorSorted : $columnHeaderColorDefault;                                                     $columnHeaderColorSorted :
                 if ($bydate) {                                                     $columnHeaderColorDefault) . "\">";
                     print 'Age';                  print "<a href=\"./" . &toggleQuery ("sortby","date") .
                 } else {                      "#dirlist\">" if (!$bydate);
                     print &link('Age', sprintf('./%s#dirlist',                  print "Age";
                                                &toggleQuery("sortby", "date")));                  print "</a>" if (!$bydate);
                 }  
                 print "</th>";                  print "</th>";
                 if ($show_author) {                  if ($show_author) {
                     $infocols++;                      $infocols++;
                     printf '<th align=left bgcolor="%s">',                      print "<th align=left bgcolor=\"" . (($byauthor) ?
                       $byauthor ? $columnHeaderColorSorted : $columnHeaderColorDefault;                                                     $columnHeaderColorSorted :
                     if ($byauthor) {                                                     $columnHeaderColorDefault) . "\">";
                         print 'Author';                      print "<a href=\"./" . &toggleQuery ("sortby","author") .
                     } else {                              "#dirlist\">" if (!$byauthor);
                         print &link('Author', sprintf('./%s#dirlist',                      print "Author";
                                                       &toggleQuery("sortby", "author")));                      print "</a>" if (!$byauthor);
                     }  
                     print "</th>";                      print "</th>";
                 }                  }
                 $infocols++;                  $infocols++;
                 printf '<th align=left bgcolor="%s">',                  print "<th align=left bgcolor=\"" . (($bylog) ?
                   $bylog ? $columnHeaderColorSorted : $columnHeaderColorDefault;                                                 $columnHeaderColorSorted :
                 if ($bylog) {                                                 $columnHeaderColorDefault) . "\">";
                     print 'Last log entry';                  print "<a href=\"./", toggleQuery("sortby","log"), "#dirlist\">" if (!$bylog);
                 } else {                  print "Last log entry";
                     print &link('Last log entry', sprintf('./%s#dirlist',                  print "</a>" if (!$bylog);
                                                           &toggleQuery("sortby", "log")));  
                 }  
                 print "</th>";                  print "</th>";
             }              }
             elsif ($use_descriptions) {              elsif ($use_descriptions) {
                 printf '<th align=left bgcolor="%s">', $columnHeaderColorDefault;                  print "<th align=left bgcolor=\"". $columnHeaderColorDefault . "\">";
                 print "Description";                  print "Description";
                 $infocols++;                  $infocols++;
             }              }
Line 700  if (-d $fullname) {
Line 528  if (-d $fullname) {
             closedir($dh);              closedir($dh);
         }          }
   
         my $hideAtticToggleLink = $input{'hideattic'} ? '' :          my $hideAtticToggleLink = "<a href=\"./" .
           &link('[Hide]', sprintf('./%s#dirlist',                  &toggleQuery ("hideattic") .
                                   &toggleQuery ("hideattic")));                  "#dirlist\">[Hide]</a>" if (!$input{'hideattic'});
   
         # Sort without the Attic/ pathname.          # Sort without the Attic/ pathname.
         # place directories first          # place directories first
Line 738  if (-d $fullname) {
Line 566  if (-d $fullname) {
   
             if ($_ eq '..' || -d "$fullname/$_") {              if ($_ eq '..' || -d "$fullname/$_") {
                 next if ($_ eq '..' && $where eq '/');                  next if ($_ eq '..' && $where eq '/');
                 my ($rev,$date,$log,$author,$filename);                  my ($rev,$date,$log,$author,$filename) = @{$fileinfo{$_}}
                 ($rev,$date,$log,$author,$filename) = @{$fileinfo{$_}}  
                     if (defined($fileinfo{$_}));                      if (defined($fileinfo{$_}));
                 printf '<tr bgcolor="%s"><td>', $tabcolors[$dirrow % 2] if $dirtable;                  print "<tr bgcolor=\"" . @tabcolors[$dirrow%2] . "\"><td>" if ($dirtable);
                 if ($_ eq '..') {                  if ($_ eq '..') {
                     $url = "../$query";                      $url = "../" . $query;
                     if ($nofilelinks) {                      if ($nofilelinks) {
                         print $backicon;                          print $backicon;
                     }                      }
                     else {                      else {
                         print &link($backicon, $url);                          print &link($backicon,$url);
                     }                      }
                     print " ", &link("Previous Directory", $url);                      print " ", &link("Previous Directory",$url);
                 }                  }
                 else {                  else {
                     $url = urlencode($_) . "/$query";                      $url = urlencode($_) . '/' . $query;
                     print "<A NAME=\"$_\"></A>";                      print "<A NAME=\"$_\"></A>";
                     if ($nofilelinks) {                      if ($nofilelinks) {
                         print $diricon;                          print $diricon;
                     }                      }
                     else {                      else {
                         print &link($diricon, $url);                          print &link($diricon,$url);
                     }                      }
                     print " ", &link("$_/", $url), $attic;                      print " ", &link($_ . "/", $url), $attic;
                     if ($_ eq "Attic") {                      if ($_ eq "Attic") {
                         print "&nbsp; ";                          print "&nbsp; <a href=\"./" .
                         print &link("[Don't hide]", sprintf('./%s#dirlist',                              &toggleQuery ("hideattic") .
                                                             &toggleQuery ("hideattic")));                                  "#dirlist\">[Don't hide]</a>";
                     }                      }
                 }                  }
                 # Show last change in dir                  # Show last change in dir
                 if ($filename) {                  if ($filename) {
                     print "</td><td>&nbsp;</td><td>&nbsp;" if ($dirtable);                      print "</td><td>&nbsp;</td><td>&nbsp;" if ($dirtable);
                     if ($date) {                      if ($date) {
                         print " <i>", readableTime(time() - $date,0), "</i>";                          print " <i>" . readableTime(time() - $date,0) . "</i>";
                     }                      }
                     if ($show_author) {                      if ($show_author) {
                         print "</td><td>&nbsp;" if ($dirtable);                          print "</td><td>&nbsp;" if ($dirtable);
Line 783  if (-d $fullname) {
Line 610  if (-d $fullname) {
                     print "$filename/$rev";                      print "$filename/$rev";
                     print "<BR>" if ($dirtable);                      print "<BR>" if ($dirtable);
                     if ($log) {                      if ($log) {
                         print "&nbsp;<font size=-1>",                          print "&nbsp;<font size=-1>"
                           &htmlify(substr($log,0,$shortLogLen));                              . &htmlify(substr($log,0,$shortLogLen));
                         if (length $log > 80) {                          if (length $log > 80) {
                             print "...";                              print "...";
                         }                          }
Line 794  if (-d $fullname) {
Line 621  if (-d $fullname) {
                 else {                  else {
                     my ($dwhere) = ($where ne "/" ? $where : "") . $_;                      my ($dwhere) = ($where ne "/" ? $where : "") . $_;
                     if ($use_descriptions && defined $descriptions{$dwhere}) {                      if ($use_descriptions && defined $descriptions{$dwhere}) {
                         print "<TD COLSPAN=", ($infocols-1), ">&nbsp;" if $dirtable;                          print "<TD COLSPAN=" . ($infocols-1) . ">&nbsp;" if $dirtable;
                         print $descriptions{$dwhere};                          print $descriptions{$dwhere};
                     } elsif ($dirtable && $infocols > 1) {                      } elsif ($dirtable && $infocols > 1) {
                         # close the row with the appropriate number of                          # close the row with the appropriate number of
Line 825  if (-d $fullname) {
Line 652  if (-d $fullname) {
                 next if (!defined($fileinfo{$_}));                  next if (!defined($fileinfo{$_}));
                 ($rev,$date,$log,$author) = @{$fileinfo{$_}};                  ($rev,$date,$log,$author) = @{$fileinfo{$_}};
                 $filesfound++;                  $filesfound++;
                 printf '<tr bgcolor="%s"><td>', $tabcolors[$dirrow % 2] if $dirtable;                  print "<tr bgcolor=\"" . @tabcolors[$dirrow%2] . "\"><td>" if ($dirtable);
                 print "<A NAME=\"$_\"></A>";                  print "<A NAME=\"$_\"></A>";
                 if ($nofilelinks) {                  if ($nofilelinks) {
                     print $fileicon;                      print $fileicon;
Line 836  if (-d $fullname) {
Line 663  if (-d $fullname) {
                 print " ", &link($_, $url), $attic;                  print " ", &link($_, $url), $attic;
                 print "</td><td>&nbsp;" if ($dirtable);                  print "</td><td>&nbsp;" if ($dirtable);
                 download_link($fileurl,                  download_link($fileurl,
                               $rev, $rev,                          $rev, $rev,
                               $defaultViewable ? "text/x-cvsweb-markup" : undef);                          $defaultViewable ? "text/x-cvsweb-markup" : undef);
                 print "</td><td>&nbsp;" if ($dirtable);                  print "</td><td>&nbsp;" if ($dirtable);
                 if ($date) {                  if ($date) {
                     print " <i>", readableTime(time() - $date,0), "</i>";                      print " <i>" . readableTime(time() - $date,0) . "</i>";
                 }                  }
                 if ($show_author) {                  if ($show_author) {
                     print "</td><td>&nbsp;" if ($dirtable);                      print "</td><td>&nbsp;" if ($dirtable);
Line 848  if (-d $fullname) {
Line 675  if (-d $fullname) {
                 }                  }
                 print "</td><td>&nbsp;" if ($dirtable);                  print "</td><td>&nbsp;" if ($dirtable);
                 if ($log) {                  if ($log) {
                     print " <font size=-1>", &htmlify(substr($log,0,$shortLogLen));                      print " <font size=-1>" . &htmlify(substr($log,0,$shortLogLen));
                     if (length $log > 80) {                      if (length $log > 80) {
                         print "...";                          print "...";
                     }                      }
Line 863  if (-d $fullname) {
Line 690  if (-d $fullname) {
         if ($dirtable && defined($tableBorderColor)) {          if ($dirtable && defined($tableBorderColor)) {
             print "</td></tr></table>";              print "</td></tr></table>";
         }          }
         print( $dirtable == 1 ? "</table>\n" : "</menu>\n" );          print "". ($dirtable == 1) ? "</table>" : "</menu>" . "\n";
   
         if ($filesexists && !$filesfound) {          if ($filesexists && !$filesfound) {
             print "<P><B>NOTE:</B> There are $filesexists files, but none matches the current tag ($input{only_with_tag})\n";              print "<P><B>NOTE:</B> There are $filesexists files, but none matches the current tag ($input{only_with_tag})\n";
Line 883  if (-d $fullname) {
Line 710  if (-d $fullname) {
             foreach my $var (@stickyvars) {              foreach my $var (@stickyvars) {
                 print "<INPUT TYPE=HIDDEN NAME=\"$var\" VALUE=\"$input{$var}\">\n"                  print "<INPUT TYPE=HIDDEN NAME=\"$var\" VALUE=\"$input{$var}\">\n"
                     if (defined($input{$var})                      if (defined($input{$var})
                         && (!defined($DEFAULTVALUE{$var})                          && $input{$var} ne $DEFAULTVALUE{$var}
                             || $input{$var} ne $DEFAULTVALUE{$var})  
                         && $input{$var} ne ""                          && $input{$var} ne ""
                         && $var ne "only_with_tag");                          && $var ne "only_with_tag");
             }              }
Line 899  if (-d $fullname) {
Line 725  if (-d $fullname) {
                        ">$tag\n";                         ">$tag\n";
             }              }
             print "</SELECT>\n";              print "</SELECT>\n";
             print " Module path or alias:\n";  
             printf "<INPUT TYPE=TEXT NAME=\"path\" VALUE=\"%s\" SIZE=15>\n", htmlquote($where);  
             print "<INPUT TYPE=SUBMIT VALUE=\"Go\">\n";              print "<INPUT TYPE=SUBMIT VALUE=\"Go\">\n";
             print "</FORM>\n";              print "</FORM>\n";
         }          }
   
         if ($allow_tar) {  
             my($basefile) = ($where =~ m,(?:.*/)?([^/]+),);  
   
             if (defined($basefile) && $basefile ne '') {  
                 print "<HR NOSHADE>\n",  
                   "<DIV align=center>",  
                     &link("Download this directory in tarball",  
                           # Mangle the filename so browsers show a reasonable  
                           # filename to download.  
                           "$basefile.tar.gz$query".  
                           ($query ? "&" : "?")."tarball=1"),  
                             "</DIV>";  
             }  
         }  
   
         my $formwhere = $scriptwhere;          my $formwhere = $scriptwhere;
         $formwhere =~ s|Attic/?$|| if ($input{'hideattic'});          $formwhere =~ s|Attic/?$|| if ($input{'hideattic'});
   
Line 939  if (-d $fullname) {
Line 747  if (-d $fullname) {
             print "<OPTION",$byrev ? " SELECTED" : ""," VALUE=rev>Revision";              print "<OPTION",$byrev ? " SELECTED" : ""," VALUE=rev>Revision";
             print "<OPTION",$bylog ? " SELECTED" : ""," VALUE=log>Log message";              print "<OPTION",$bylog ? " SELECTED" : ""," VALUE=log>Log message";
             print "</SELECT></td>";              print "</SELECT></td>";
             print "<td>Sort log by: ";              print "<td>revisions by: \n";
             printLogSortSelect(0);              print "<SELECT NAME=logsort>\n";
             print "</td></tr>";              print "<OPTION VALUE=cvs",$logsort eq "cvs" ? " SELECTED" : "", ">Not sorted";
               print "<OPTION VALUE=date",$logsort eq "date" ? " SELECTED" : "", ">Commit date";
               print "<OPTION VALUE=rev",$logsort eq "rev" ? " SELECTED" : "", ">Revision";
               print "</SELECT></td></tr>";
             print "<tr><td>Diff format: ";              print "<tr><td>Diff format: ";
             printDiffSelect(0);              printDiffSelect(0);
             print "</td>";              print "</td>";
Line 961  if (-d $fullname) {
Line 772  if (-d $fullname) {
     elsif (-f $fullname . ',v') {      elsif (-f $fullname . ',v') {
         if (defined($input{'rev'}) || $doCheckout) {          if (defined($input{'rev'}) || $doCheckout) {
             &doCheckout($fullname, $input{'rev'});              &doCheckout($fullname, $input{'rev'});
             gzipclose();  
             exit;              exit;
         }          }
         if (defined($input{'annotate'}) && $allow_annotate) {          if (defined($input{'annotate'}) && $allow_annotate) {
             &doAnnotate($input{'annotate'});              &doAnnotate($input{'annotate'});
             gzipclose();  
             exit;              exit;
         }          }
         if (defined($input{'r1'}) && defined($input{'r2'})) {          if (defined($input{'r1'}) && defined($input{'r2'})) {
             &doDiff($fullname, $input{'r1'}, $input{'tr1'},              &doDiff($fullname, $input{'r1'}, $input{'tr1'},
                     $input{'r2'}, $input{'tr2'}, $input{'f'});                      $input{'r2'}, $input{'tr2'}, $input{'f'});
             gzipclose();  
             exit;              exit;
         }          }
         print("going to dolog($fullname)\n") if ($verbose);          print("going to dolog($fullname)\n") if ($verbose);
Line 995  if (-d $fullname) {
Line 803  if (-d $fullname) {
         # e.g. foo.c          # e.g. foo.c
         &doDiff($fullname, $input{'r1'}, $input{'tr1'},          &doDiff($fullname, $input{'r1'}, $input{'tr1'},
                 $input{'r2'}, $input{'tr2'}, $input{'f'});                  $input{'r2'}, $input{'tr2'}, $input{'f'});
         gzipclose();  
         exit;          exit;
     }      }
     elsif (($newname = $fullname) =~ s|/([^/]+)$|/Attic/$1| &&      elsif (($newname = $fullname) =~ s|/([^/]+)$|/Attic/$1| &&
Line 1003  if (-d $fullname) {
Line 810  if (-d $fullname) {
         # The file has been removed and is in the Attic.          # The file has been removed and is in the Attic.
         # Send a redirect pointing to the file in the Attic.          # Send a redirect pointing to the file in the Attic.
         (my $newplace = $scriptwhere) =~ s|/([^/]+)$|/Attic/$1|;          (my $newplace = $scriptwhere) =~ s|/([^/]+)$|/Attic/$1|;
         redirect("$newplace$query");          &redirect($newplace);
         exit;          exit;
     }      }
     elsif (0 && (my @files = &safeglob($fullname . ",v"))) {      elsif (0 && (my @files = &safeglob($fullname . ",v"))) {
Line 1017  if (-d $fullname) {
Line 824  if (-d $fullname) {
         my $fh = do {local(*FH);};          my $fh = do {local(*FH);};
         my ($xtra, $module);          my ($xtra, $module);
         # Assume it's a module name with a potential path following it.          # Assume it's a module name with a potential path following it.
         $xtra = (($module = $where) =~ s|/.*||) ? $& : '';          $xtra = $& if (($module = $where) =~ s|/.*||);
         # Is there an indexed version of modules?          # Is there an indexed version of modules?
         if (open($fh, "< $cvsroot/CVSROOT/modules")) {          if (open($fh, "$cvsroot/CVSROOT/modules")) {
             while (<$fh>) {              while (<$fh>) {
                 if (/^(\S+)\s+(\S+)/o && $module eq $1                  if (/^(\S+)\s+(\S+)/o && $module eq $1
                     && -d "$cvsroot/$2" && $module ne $2) {                      && -d "${cvsroot}/$2" && $module ne $2) {
                     redirect("$scriptname/$2$xtra$query");                      &redirect($scriptname . '/' . $2 . $xtra);
                 }                  }
             }              }
         }          }
         &fatal("404 Not Found","$where: no such file or directory");          &fatal("404 Not Found","$where: no such file or directory");
     }      }
   
 gzipclose();  
 ## End MAIN  ## End MAIN
   
 sub printDiffSelect($) {  sub printDiffSelect($) {
     my ($use_java_script) = @_;      my ($use_java_script) = @_;
     my $f = $input{'f'};      my ($f) = $input{'f'};
       print "<SELECT NAME=\"f\"";
     print '<SELECT NAME="f"';      print " onchange=\"submit()\"" if ($use_java_script);
     print ' onchange="submit()"' if $use_java_script;      print ">\n";
     print '>';      print "<OPTION VALUE=h",$f eq "h" ? " SELECTED" : "", ">Colored Diff";
       print "<OPTION VALUE=H",$f eq "H" ? " SELECTED" : "", ">Long Colored Diff";
     local $_;      print "<OPTION VALUE=u",$f eq "u" ? " SELECTED" : "", ">Unidiff";
     for (@DIFFTYPES) {      print "<OPTION VALUE=c",$f eq "c" ? " SELECTED" : "", ">Context Diff";
         printf('<OPTION VALUE="%s"%s>%s',      print "<OPTION VALUE=s",$f eq "s" ? " SELECTED" : "", ">Side by Side";
                $_,  
                $f eq $_ ? ' SELECTED' : '',  
                "\u$DIFFTYPES{$_}{'descr'}"  
               );  
     }  
   
     print "</SELECT>";      print "</SELECT>";
 }  }
   
 sub printLogSortSelect($) {  
     my ($use_java_script) = @_;  
   
     print '<SELECT NAME="logsort"';  
     print ' onchange="submit()"' if $use_java_script;  
     print '>';  
   
     local $_;  
     for (@LOGSORTKEYS) {  
         printf('<OPTION VALUE="%s"%s>%s',  
                $_,  
                $logsort eq $_ ? ' SELECTED' : '',  
                "\u$LOGSORTKEYS{$_}{'descr'}"  
               );  
     }  
   
     print "</SELECT>";  
 }  
   
 sub findLastModifiedSubdirs(@) {  sub findLastModifiedSubdirs(@) {
     my (@dirs) = @_;      my (@dirs) = @_;
     my ($dirname, @files);      my ($dirname, @files);
Line 1106  sub findLastModifiedSubdirs(@) {
Line 886  sub findLastModifiedSubdirs(@) {
     return @files;      return @files;
 }  }
   
 sub htmlify_sub(&$) {  
     (my $proc, local $_) = @_;  
     local @_ = split(m`(<a [^>]+>[^<]*</a>)`i);  
     my $linked;  
     my $result = '';  
   
     while (($_, $linked) = splice(@_, 0, 2)) {  
         &$proc();  
         $result .= $_ if defined($_);  
         $result .= $linked if defined($linked);  
     }  
   
     $result;  
 }  
   
 sub htmlify($;$) {  sub htmlify($;$) {
     (local $_, my $extra) = @_;          my($string, $extra) = @_;
   
     $_ = htmlquote($_);          # Special Characters; RFC 1866
           $string =~ s/&/&amp;/g;
           $string =~ s/\"/&quot;/g;
           $string =~ s/</&lt;/g;
           $string =~ s/>/&gt;/g;
   
     # get URL's as link          # get URL's as link ..
     s{          $string =~ s`(http|ftp|https)(://[-a-zA-Z0-9%.~:_/]+)([?&]([-a-zA-Z0-9%.~:_]+)=([-a-zA-Z0-9%.~:_])+)*`<A HREF="$1$2$3">$1$2$3</A>`g;
       (http|ftp|https)://\S+          # get e-mails as link
      }{          $string =~ s`([-a-zA-Z0-9_.]+@([-a-zA-Z0-9]+\.)+[A-Za-z]{2,4})`<A HREF="mailto:$1">$1</A>`g;
          &link($&, htmlunquote($&))  
      }egx;  
   
     # get e-mails as link          if ($extra) {
     $_ = htmlify_sub {              # get PR #'s as link ..
         s<              if (defined($prcgi)) {
           [\w+=\-.!]+@[\w\-]+(\.[\w\-]+)+                  1 while $string =~ s`\b(pr[:#]?\s*(?:#?\d+[,\s]\s*)*#?)(\d+)\b`sprintf('%s<A HREF="%s">%s</A>', $1, sprintf($prcgi, $2), $2)`ie;
             ><                  $string =~ s`\b${prcategories}/(\d+)\b`sprintf('<A HREF="%s">%s</A>', sprintf($prcgi, $1), $&)`igeo;
               &link($&, "mailto:$&")              }
                 >egix;  
     } $_;  
   
     if ($extra) {              # get manpage specs as link ..
         # get PR #'s as link: "PR#nnnn" "PR: nnnn, ..." "PR nnnn, ..." "bin/nnnn"              if (defined($mancgi)) {
         if (defined($prcgi) && defined($re_prcategories) && defined($re_prkeyword)) {                  $string =~ s`\b([a-zA-Z]\w+)\(([0-9n])\)\B`sprintf('<A HREF="%s">%s</A>', sprintf($mancgi, $2, $1), $&)`ge;
             my $prev;              }
   
             do {  
                 $prev = $_;  
   
                 $_ = htmlify_sub {  
                     s{  
                       (\b$re_prkeyword[:\#]?\s*  
                        (?:  
                         \#?  
                         \d+[,\s]\s*  
                        )*  
                        \#?)  
                       (\d+)\b  
                      }{  
                          $1 . &link($2, sprintf($prcgi, $2))  
                      }egix;  
                 } $_;  
             } while ($_ ne $prev);  
   
             $_ = htmlify_sub {  
                 s{  
                   (\b$re_prcategories/(\d+)\b)  
                  }{  
                      &link($1, sprintf($prcgi, $2))  
                  }egox;  
             } $_;  
         }          }
   
         # get manpage specs as link: "foo.1" "foo(1)"          return $string;
         if (defined($mancgi)) {  
             $_ = htmlify_sub {  
                 s{  
                   (\b([a-zA-Z][\w.]+)  
                    (?:  
                     \( ([0-9n]) \)\B  
                     |  
                     \.([0-9n])\b  
                    )  
                   )  
                  }{  
                      &link($1, sprintf($mancgi, defined($3) ? $3 : $4, $2))  
                  }egx;  
             } $_;  
         }  
     }  
   
     $_;  
 }  }
   
 sub spacedHtmlText($;$) {  sub spacedHtmlText($) {
         local $_ = $_[0];          local $_ = $_[0];
         my $ts = $_[1] || $tabstop;  
   
         # Cut trailing spaces and tabs          # Cut trailing spaces
         s/[ \t]+$//;          s/\s+$/\n/;
   
         if (defined($ts)) {          # Expand tabs
             # Expand tabs          s/\t+/' ' x (length($&) * $tabstop - length($`) % $tabstop)/e
             1 while s/\t+/' ' x (length($&) * $ts - length($`) % $ts)/e              if (defined($tabstop));
         }  
   
         # replace <tab> and <space> (\001 is to protect us from htmlify)          # replace <tab> and <space> (\001 is to protect us from htmlify)
         # gzip can make excellent use of this repeating pattern :-)          # gzip can make excellent use of this repeating pattern :-)
           s/\001/\001%/g; #protect our & substitute
         if ($hr_breakable) {          if ($hr_breakable) {
             # make every other space 'breakable'              # make every other space 'breakable'
               s/  / \001nbsp; \001nbsp; \001nbsp; \001nbsp;/g;    # <tab>
             s/  / \001nbsp;/g;                              # 2 * <space>              s/  / \001nbsp;/g;                              # 2 * <space>
             # leave single space as it is              # leave single space as it is
         } else {          }
           else {
               s/  /\001nbsp;\001nbsp;\001nbsp;\001nbsp;\001nbsp;\001nbsp;\001nbsp;\001nbsp;/g;
             s/ /\001nbsp;/g;              s/ /\001nbsp;/g;
         }          }
   
         $_ = htmlify($_);          $_ = htmlify($_);
   
         # unescape          # unescape
         y/\001/&/;          s/\001([^%])/&$1/g;
           s/\001%/\001/g;
   
         return $_;          return $_;
 }  }
   
 sub link($$) {  sub link($$) {
         my($name, $url) = @_;          my($name, $where) = @_;
   
         sprintf '<A HREF="%s">%s</A>', hrefquote($url), $name;          return "<A HREF=\"$where\">$name</A>\n";
 }  }
   
 sub revcmp($$) {  sub revcmp($$) {
Line 1276  sub redirect($) {
Line 1000  sub redirect($) {
                 print "Location: $url\r\n";                  print "Location: $url\r\n";
         }          }
         html_header("Moved");          html_header("Moved");
         print "This document is located ", &link('here', $url), "\n";          print "This document is located <A HREF=$url>here</A>.\n";
         print &html_footer;          print &html_footer;
         exit(1);          exit(1);
 }  }
Line 1302  sub safeglob($) {
Line 1026  sub safeglob($) {
                 $glob =~ s/{([^}]+)}/($t = $1) =~ s-,-|-g; "($t)"/eg;                  $glob =~ s/{([^}]+)}/($t = $1) =~ s-,-|-g; "($t)"/eg;
                 foreach (readdir($dh)) {                  foreach (readdir($dh)) {
                         if (/^${glob}$/) {                          if (/^${glob}$/) {
                                 push(@results, "$dirname/" .$_);                                  push(@results, $dirname . "/" .$_);
                         }                          }
                 }                  }
         }          }
Line 1310  sub safeglob($) {
Line 1034  sub safeglob($) {
         @results;          @results;
 }  }
   
 sub search_path($) {  
     my($command) = @_;  
     my $d;  
   
     for $d (split(/:/, $command_path)) {  
         return "$d/$command" if -x "$d/$command";  
     }  
   
     $command;  
 }  
   
 sub getMimeTypeFromSuffix($) {  sub getMimeTypeFromSuffix($) {
     my ($fullname) = @_;      my ($fullname) = @_;
     my ($mimetype, $suffix);      my ($mimetype, $suffix);
Line 1351  sub getMimeTypeFromSuffix($) {
Line 1064  sub getMimeTypeFromSuffix($) {
 }  }
   
 ###############################  ###############################
 # read first lines like head(1)  
 ###############################  
 sub head($;$) {  
     my $fh = $_[0];  
     my $linecount = $_[1] || 10;  
   
     my @buf;  
   
     if ($linecount > 0) {  
         my $i;  
         for ($i = 0; !eof($fh) && $i < $linecount; $i++) {  
             push @buf, scalar <$fh>;  
         }  
     } else {  
         @buf = <$fh>;  
     }  
   
     @buf;  
 }  
   
 ###############################  
 # scan vim and Emacs directives  
 ###############################  
 sub scan_directives(@) {  
     my $ts = undef;  
   
     for (@_) {  
         $ts = $1 if /\b(?:ts|tabstop|tab-width)[:=]\s*([1-9]\d*)\b/;  
     }  
   
     ('tabstop' => $ts);  
 }  
   
 ###############################  
 # show Annotation  # show Annotation
 ###############################  ###############################
 sub doAnnotate($$) {  sub doAnnotate($$) {
Line 1394  sub doAnnotate($$) {
Line 1073  sub doAnnotate($$) {
     my $reader = do {local(*FH);};      my $reader = do {local(*FH);};
     my $writer = do {local(*FH);};      my $writer = do {local(*FH);};
   
     # make sure the revisions are wellformed, for security      # make sure the revisions a wellformed, for security
     # reasons ..      # reasons ..
     if ($rev =~ /[^\w.]/) {      if ($rev =~ /[^\w.]/) {
         &fatal("404 Not Found",          &fatal("404 Not Found",
Line 1404  sub doAnnotate($$) {
Line 1083  sub doAnnotate($$) {
     ($pathname = $where) =~ s/(Attic\/)?[^\/]*$//;      ($pathname = $where) =~ s/(Attic\/)?[^\/]*$//;
     ($filename = $where) =~ s/^.*\///;      ($filename = $where) =~ s/^.*\///;
   
       http_header();
   
       navigateHeader($scriptwhere,$pathname,$filename,$rev, "annotate");
       print "<h3 align=center>Annotation of $pathname$filename, Revision $rev</h3>\n";
   
     # this seems to be necessary      # this seems to be necessary
     $| = 1; $| = 0; # Flush      $| = 1; $| = 0; # Flush
   
Line 1413  sub doAnnotate($$) {
Line 1097  sub doAnnotate($$) {
     # the public domain.      # the public domain.
     # we could abandon the use of rlog, rcsdiff and co using      # we could abandon the use of rlog, rcsdiff and co using
     # the cvsserver in a similiar way one day (..after rewrite)      # the cvsserver in a similiar way one day (..after rewrite)
     $pid = open2($reader, $writer, "cvs", @cvs_options, "server")      $pid = open2($reader, $writer, "cvs server") || fatal ("500 Internal Error",
       || fatal ("500 Internal Error", "Fatal Error - unable to open cvs for annotation");                                                                 "Fatal Error - unable to open cvs for annotation");
   
     # OK, first send the request to the server.  A simplified example is:      # OK, first send the request to the server.  A simplified example is:
     #     Root /home/kingdon/zwork/cvsroot      #     Root /home/kingdon/zwork/cvsroot
Line 1465  sub doAnnotate($$) {
Line 1149  sub doAnnotate($$) {
     # were nicer about buffering, then we could just leave it open, I think.      # were nicer about buffering, then we could just leave it open, I think.
     close ($writer) || die "cannot close: $!";      close ($writer) || die "cannot close: $!";
   
     http_header();  
   
     navigateHeader($scriptwhere,$pathname,$filename,$rev, "annotate");  
     print "<h3 align=center>Annotation of $pathname$filename, Revision $rev</h3>\n";  
   
     # Ready to get the responses from the server.      # Ready to get the responses from the server.
     # For example:      # For example:
     #     E Annotations for foo/xx      #     E Annotations for foo/xx
Line 1485  sub doAnnotate($$) {
Line 1164  sub doAnnotate($$) {
     else {      else {
         print "<pre>";          print "<pre>";
     }      }
       while (<$reader>) {
     # prefetch several lines  
     my @buf = head($reader);  
   
     my %d = scan_directives(@buf);  
   
     while (@buf || !eof($reader)) {  
         $_ = @buf ? shift @buf : <$reader>;  
   
         my @words = split;          my @words = split;
         # Adding one is for the (single) space which follows $words[0].          # Adding one is for the (single) space which follows $words[0].
         my $rest = substr ($_, length ($words[0]) + 1);          my $rest = substr ($_, length ($words[0]) + 1);
Line 1502  sub doAnnotate($$) {
Line 1173  sub doAnnotate($$) {
         }          }
         elsif ($words[0] eq "M") {          elsif ($words[0] eq "M") {
             $lineNr++;              $lineNr++;
             (my $lrev = substr($_, 2, 13)) =~ y/ //d;              my $lrev = substr ($_, 2, 13);
             (my $lusr = substr($_, 16,  9)) =~ y/ //d;              my $lusr = substr ($_, 16,  9);
             my $line = substr($_, 36);              my $line = substr ($_, 36);
             my $isCurrentRev = ($rev eq $lrev);  
             # we should parse the date here ..              # we should parse the date here ..
             if ($lrev eq $oldLrev) {              if ($lrev eq $oldLrev) {
                 $revprint = sprintf('%-8s', '');                  $revprint = "             ";
             }              }
             else {              else {
                 $revprint = sprintf('%-8s', $lrev);                  $revprint = $lrev; $oldLusr = "";
                 $revprint =~ s`\S+`&link($&, "$scriptwhere$query#rev$&")`e;     # `  
                 $oldLusr = '';  
             }              }
             if ($lusr eq $oldLusr) {              if ($lusr eq $oldLusr) {
                 $usrprint = '';                  $usrprint = "         ";
             }              }
             else {              else {
                 $usrprint = $lusr;                  $usrprint = $lusr;
             }              }
             $oldLrev = $lrev;              $oldLrev = $lrev;
             $oldLusr = $lusr;              $oldLusr = $lusr;
               # is there a less timeconsuming way to strip spaces ?
               ($lrev = $lrev) =~ s/\s+//g;
               my $isCurrentRev = ($rev eq $lrev);
   
             # Set bold for text-based browsers only - graphical              print "<b>" if ($isCurrentRev);
             # browsers show bold fonts a bit wider than regular fonts,              printf ("%8s%s%8s %4d:", $revprint, ($isCurrentRev ? "|" : " "), $usrprint, $lineNr);
             # so it looks irregular.              print spacedHtmlText($line);
             print "<b>" if ($isCurrentRev && $is_textbased);              print "</b>" if ($isCurrentRev);
   
             printf "%s%s %-8s %4d:",  
                     $revprint,  
                     $isCurrentRev ? '!' : ' ',  
                     $usrprint,  
                     $lineNr;  
             print spacedHtmlText($line, $d{'tabstop'});  
   
             print "</b>" if ($isCurrentRev && $is_textbased);  
         }          }
         elsif ($words[0] eq "ok") {          elsif ($words[0] eq "ok") {
             # We could complain about any text received after this, like the              # We could complain about any text received after this, like the
             # CVS command line client.  But for simplicity, we don't.              # CVS command line client.  But for simplicity, we don't.
         }          }
         elsif ($words[0] eq "error") {          elsif ($words[0] eq "error") {
             fatal("500 Internal Error", "Error occured during annotate: <b>$_</b>");              fatal ("500 Internal Error", "Error occured during annotate: <b>$_</b>");
         }          }
     }      }
     if ($annTable) {      if ($annTable) {
Line 1605  sub doCheckout($$) {
Line 1267  sub doCheckout($$) {
     #      #
     # Safely for a child process to read from.      # Safely for a child process to read from.
     if (! open($fh, "-|")) { # child      if (! open($fh, "-|")) { # child
         open(STDERR, ">&STDOUT"); # Redirect stderr to stdout        open(STDERR, ">&STDOUT"); # Redirect stderr to stdout
         exec($CMD{cvs}, @cvs_options, '-d', $cvsroot, 'co', '-p', $revopt, $where);        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  #Checking out squid/src/ftp.c
 #RCS:  /usr/src/CVS/squid/src/ftp.c,v  #RCS:  /usr/src/CVS/squid/src/ftp.c,v
Line 1632  sub doCheckout($$) {
Line 1289  sub doCheckout($$) {
     }      }
     if ($filename ne $where) {      if ($filename ne $where) {
         &fatal("500 Internal Error",          &fatal("500 Internal Error",
                "Unexpected output from cvs co: $cvsheader");                 "Unexpected output from cvs co: $cvsheader"
                  . "<p><b>Check whether the directory $cvsroot/CVSROOT exists "
                  . "and the script has write-access to the CVSROOT/history "
                  . "file if it exists."
                  . "<br>The script needs to place lock files in the "
                  . "directory the file is in as well.</b>");
     }      }
     $| = 1;      $| = 1;
   
Line 1660  sub cvswebMarkup($$$) {
Line 1322  sub cvswebMarkup($$$) {
     print "<HR noshade>";      print "<HR noshade>";
     print "<table width=\"100%\"><tr><td bgcolor=\"$markupLogColor\">";      print "<table width=\"100%\"><tr><td bgcolor=\"$markupLogColor\">";
     print "File: ", &clickablePath($where, 1);      print "File: ", &clickablePath($where, 1);
     print "&nbsp;(";      print "&nbsp;";
     &download_link($fileurl, $revision, "download");      &download_link(urlencode($fileurl), $revision, "(download)");
     print ")";  
     if (!$defaultTextPlain) {      if (!$defaultTextPlain) {
         print "&nbsp;(";          print "&nbsp;";
         &download_link($fileurl, $revision, "as text",          &download_link(urlencode($fileurl), $revision, "(as text)",
                "text/plain");                 "text/plain");
         print ")";  
     }      }
     print "<BR>\n";      print "<BR>\n";
     if ($show_log_in_markup) {      if ($show_log_in_markup) {
Line 1680  sub cvswebMarkup($$$) {
Line 1340  sub cvswebMarkup($$$) {
             $input{only_with_tag};              $input{only_with_tag};
     }      }
     print "</td></tr></table>";      print "</td></tr></table>";
       my @content = <$filehandle>;
     my $url = download_url($fileurl, $revision, $mimetype);      my $url = download_url($fileurl, $revision, $mimetype);
     print "<HR noshade>";      print "<HR noshade>";
     if ($mimetype =~ /^image/) {      if ($mimetype =~ /^image/) {
         printf '<IMG SRC="%s"><BR>', hrefquote("$url$barequery");          print "<IMG SRC=\"$url$barequery\"><BR>";
     }      }
     elsif ($mimetype =~ m%^application/pdf%) {      else {
         printf '<EMBED SRC="%s" WIDTH="100%"><BR>', hrefquote("$url$barequery");  
     }  
     elsif ($preformat_in_markup) {  
         print "<PRE>";          print "<PRE>";
           foreach (@content) {
         # prefetch several lines              print spacedHtmlText($_);
         my @buf = head($filehandle);  
   
         my %d = scan_directives(@buf);  
   
         while (@buf || !eof($filehandle)) {  
             $_ = @buf ? shift @buf : <$filehandle>;  
   
             print spacedHtmlText($_, $d{'tabstop'});  
         }          }
         print "</PRE>";          print "</PRE>";
     }      }
     else {  
         print "<PLAINTEXT>\n", <$filehandle>;  
     }  
 }  }
   
 sub viewable($) {  sub viewable($) {
     my ($mimetype) = @_;      my ($mimetype) = @_;
   
     $mimetype =~ m%^((text|image)/|application/pdf)% ;      $mimetype =~ m%^(text|image)/%;
 }  }
   
 ###############################  ###############################
Line 1720  sub viewable($) {
Line 1367  sub viewable($) {
 sub doDiff($$$$$$) {  sub doDiff($$$$$$) {
         my($fullname, $r1, $tr1, $r2, $tr2, $f) = @_;          my($fullname, $r1, $tr1, $r2, $tr2, $f) = @_;
         my $fh = do {local(*FH);};          my $fh = do {local(*FH);};
         my ($rev1, $rev2, $sym1, $sym2, $f1, $f2);          my ($rev1, $rev2, $sym1, $sym2, @difftype, $diffname, $f1, $f2);
   
         if ($r1 =~ /([^:]+)(:(.+))?/) {          if ($r1 =~ /([^:]+)(:(.+))?/) {
             $rev1 = $1;              $rev1 = $1;
Line 1754  sub doDiff($$$$$$) {
Line 1401  sub doDiff($$$$$$) {
             ($rev1, $sym1) = ($rev2, $sym2);              ($rev1, $sym1) = ($rev2, $sym2);
             ($rev2, $sym2) = ($tmp1, $tmp2);              ($rev2, $sym2) = ($tmp1, $tmp2);
         }          }
         my $difftype = $DIFFTYPES{$f};          my $human_readable = 0;
           if ($f eq 'c') {
         if (!$difftype) {              @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 {
             fatal ("400 Bad arguments", "Diff format $f not understood");              fatal ("400 Bad arguments", "Diff format $f not understood");
         }          }
   
         my @difftype       = @{$difftype->{'opts'}};  
         my $human_readable = $difftype->{'colored'};  
   
         # apply special options          # apply special options
         if ($showfunc) {          if ($showfunc) {
             push @difftype, '-p' if $f ne 's';              push @difftype, '-p' if $f =~ /^[cHhu]$/;
   
             my($re1, $re2);              my($re1, $re2);
   
Line 1786  sub doDiff($$$$$$) {
Line 1451  sub doDiff($$$$$$) {
         }          }
         if (! open($fh, "-|")) { # child          if (! open($fh, "-|")) { # child
             open(STDERR, ">&STDOUT"); # Redirect stderr to stdout              open(STDERR, ">&STDOUT"); # Redirect stderr to stdout
             exec($CMD{rcsdiff}, @difftype, "-r$rev1", "-r$rev2", $fullname);              exec("rcsdiff",@difftype,"-r$rev1","-r$rev2",$fullname);
         }          }
         if ($human_readable) {          if ($human_readable) {
             http_header();              http_header();
             &human_readable_diff($fh, $rev2);              &human_readable_diff($fh, $rev2);
             gzipclose();  
             exit;              exit;
         }          }
         else {          else {
Line 1827  sub doDiff($$$$$$) {
Line 1491  sub doDiff($$$$$$) {
                 s|$cvsroot/||o;                  s|$cvsroot/||o;
                 if ($sym1) {                  if ($sym1) {
                     chop;                      chop;
                     $_ .= " $sym1\n";                      $_ .= " " . $sym1 . "\n";
                 }                  }
             }              }
             elsif (m|^$f2 $cvsroot|o) {              elsif (m|^$f2 $cvsroot|o) {
                 s|$cvsroot/||o;                  s|$cvsroot/||o;
                 if ($sym2) {                  if ($sym2) {
                     chop;                      chop;
                     $_ .= " $sym2\n";                      $_ .= " " . $sym2 . "\n";
                 }                  }
             }              }
             print $_;              print $_;
Line 1867  sub getDirLogs($$@) {
Line 1531  sub getDirLogs($$@) {
         return;          return;
     }      }
   
     if (defined($tag)) {      if ($tag) {
         #can't use -r<tag> as - is allowed in tagnames, but misinterpreated by rlog..          #can't use -r<tag> as - is allowed in tagnames, but misinterpreated by rlog..
         if (! open($fh, "-|")) {          if (! open($fh, "-|")) {
             open(STDERR, '>/dev/null'); # rlog may complain; ignore.                  open(STDERR, '>/dev/null'); # rlog may complain; ignore.
             exec($CMD{rlog}, @files);                  exec('rlog', @files);
         }          }
     }      }
     else {      else {
         if (! open($fh, "-|")) {          my $kidpid = open($fh, "-|");
             open(STDERR, '>/dev/null'); # rlog may complain; ignore.          if (! $kidpid) {
             exec($CMD{rlog}, '-r', @files);                  open(STDERR, '>/dev/null'); # rlog may complain; ignore.
                   exec('rlog', '-r', @files);
         }          }
     }      }
     $state = "start";      $state = "start";
     while (<$fh>) {      while (<$fh>) {
         if ($state eq "start") {          if ($state eq "start") {
             #Next file. Initialize file variables              #Next file. Initialize file variables
             $rev = '';              $rev = undef;
             $revwanted = '';              $revwanted = undef;
             $branch = '';              $branch = undef;
             $branchpoint = '';              $branchpoint = undef;
             $filename = '';              $filename = undef;
             $log = '';              $log = undef;
             $revision = '';              $revision = undef;
               $branch = undef;
             %symrev = ();              %symrev = ();
             @filetags = ();              @filetags = ();
             #jump to head state              #jump to head state
Line 1900  sub getDirLogs($$@) {
Line 1566  sub getDirLogs($$@) {
 again:  again:
         if ($state eq "head") {          if ($state eq "head") {
             #$rcsfile = $1 if (/^RCS file: (.+)$/); #not used (yet)              #$rcsfile = $1 if (/^RCS file: (.+)$/); #not used (yet)
               $filename = $1 if (/^Working file: (.+)$/);
             if (/^Working file: (.+)$/) {              $head = $1 if (/^head: (.+)$/);
                 $filename = $1;              $branch = $1 if (/^branch: (.+)$/);
             } elsif (/^head: (.+)$/) {          }
                 $head = $1;          if ($state eq "head" && /^symbolic names/) {
             } elsif (/^branch: (.+)$/) {              $state = "tags";
                 $branch = $1              ($branch = $head) =~ s/\.\d+$// if (!defined($branch));
             } elsif (/^symbolic names:/) {              $branch =~ s/(\.?)(\d+)$/${1}0.$2/;
                 $state = "tags";              $symrev{MAIN} = $branch;
                 ($branch = $head) =~ s/\.\d+$// if $branch eq '';              $symrev{HEAD} = $branch;
                 $branch =~ s/(\d+)$/0.$1/;              $alltags{MAIN} = 1;
                 $symrev{MAIN} = $branch;              $alltags{HEAD} = 1;
                 $symrev{HEAD} = $branch;              push (@filetags, "MAIN", "HEAD");
                 $alltags{MAIN} = 1;  
                 $alltags{HEAD} = 1;  
                 push (@filetags, "MAIN", "HEAD");  
             } elsif (/$LOG_REVSEPARATOR/o) {  
                 $state = "log";  
                 $rev = '';  
                 $date = '';  
                 $log = '';  
                 # Try to reconstruct the relative filename if RCS spits out a full path  
                 $filename =~ s%^\Q$DirName\E/%%;  
             }  
             next;              next;
         }          }
         if ($state eq "tags") {          if ($state eq "tags" &&
             if (/^\s+(.+):\s+([\d\.]+)\s+$/) {                              /^\s+(.+):\s+([\d\.]+)\s+$/) {
                 push (@filetags, $1);              push (@filetags, $1);
                 $symrev{$1} = $2;              $symrev{$1} = $2;
                 $alltags{$1} = 1;              $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";
                 next;                  next;
             } elsif (/^\S/) {  
                 if (defined($tag)) {  
                     if(defined($symrev{$tag}) || $tag eq "HEAD") {  
                         $revwanted = $symrev{$tag eq "HEAD" ? "MAIN" : $tag};  
                         ($branch = $revwanted) =~ s/\b0\.//;  
                         ($branchpoint = $branch) =~ s/\.?\d+$//;  
                         $revwanted = '' if ($revwanted ne $branch);  
                     } 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 ($state eq "log") {
             if (/$LOG_REVSEPARATOR/o || /$LOG_FILESEPARATOR/o) {              if (/^----------------------------$/
                   || /^=============================/) {
                 # End of a log entry.                  # End of a log entry.
                 my $revbranch = $rev;                  my $revbranch;
                 $revbranch =~ s/\.\d+$//;                  ($revbranch = $rev) =~ s/\.\d+$//;
                 print "$filename $rev Wanted: $revwanted ",                  print "$filename $rev Wanted: $revwanted "
                   "Revbranch: $revbranch Branch: $branch ",                      . "Revbranch: $revbranch Branch: $branch "
                     "Branchpoint: $branchpoint\n" if ($verbose);                      . "Branchpoint: $branchpoint\n" if ($verbose);
                 if ($revwanted eq '' && $branch ne ''                  if (!defined($revwanted) && defined($branch)
                     && $branch eq $revbranch || !defined($tag)) {                      && $branch eq $revbranch || !defined($tag)) {
                     print "File revision $rev found for branch $branch\n"                      print "File revision $rev found for branch $branch\n"
                         if ($verbose);                          if ($verbose);
                     $revwanted = $rev;                      $revwanted = $rev;
                 }                  }
                 if ($revwanted ne '' ? $rev eq $revwanted :                  if (defined($revwanted) ? $rev eq $revwanted :
                     $branchpoint ne '' ? $rev eq $branchpoint :                      defined($branchpoint) ? $rev eq $branchpoint :
                     0 && ($rev eq $head)) { # Don't think head is needed here..                      0 && ($rev eq $head)) { # Don't think head is needed here..
                     print "File info $rev found for $filename\n" if ($verbose);                      print "File info $rev found for $filename\n" if ($verbose);
                     my @finfo = ($rev,$date,$log,$author,$filename);                      my @finfo = ($rev,$date,$log,$author,$filename);
Line 1976  again:
Line 1640  again:
                     $fileinfo{$name} = [ @finfo ];                      $fileinfo{$name} = [ @finfo ];
                     $state = "done" if ($rev eq $revwanted);                      $state = "done" if ($rev eq $revwanted);
                 }                  }
                 $rev = '';                  $rev = undef;
                 $date = '';                  $date = undef;
                 $log = '';                  $log = "";
             }              }
             elsif ($date eq '' && m|^date:\s+(\d+)/(\d+)/(\d+)\s+(\d+):(\d+):(\d+);|) {              elsif (!defined($date) && m|^date:\s+(\d+)/(\d+)/(\d+)\s+(\d+):(\d+):(\d+);|) {
                 my $yr = $1;                  my $yr = $1;
                 # damn 2-digit year routines :-)                  # damn 2-digit year routines :-)
                 if ($yr > 100) {                  if ($yr > 100) {
Line 1992  again:
Line 1656  again:
                 $log = '';                  $log = '';
                 next;                  next;
             }              }
             elsif ($rev eq '' && /^revision (.*)$/) {              elsif (!defined($rev) && m/^revision (.*)$/) {
                 $rev = $1;                  $rev = $1;
                 next;                  next;
             }              }
             else {              else {
                 $log .= $_;                  $log = $log . $_;
             }              }
         }          }
         if (/$LOG_FILESEPARATOR/o) {          if (/^===============/) {
             $state = "start";              $state = "start";
             next;              next;
         }          }
     }      }
     if ($. == 0) {      if ($. == 0) {
         fatal("500 Internal Error",          fatal("500 Internal Error",
               "Failed to spawn GNU rlog on <em>'".join(", ", @files)."'</em><p>Did you set the <b>\$command_path</b> in your configuration file correctly ? (Currently '$command_path'");                "Failed to spawn GNU rlog on <em>'".join(", ", @files)."'</em><p>did you set the <b>\$ENV{PATH}</b> in your configuration file correctly ?");
     }      }
     close($fh);      close($fh);
 }  }
Line 2035  sub readLog($;$) {
Line 1699  sub readLog($;$) {
   
         print("Going to rlog '$fullname'\n") if ($verbose);          print("Going to rlog '$fullname'\n") if ($verbose);
         if (! open($fh, "-|")) { # child          if (! open($fh, "-|")) { # child
             if ($revision ne '') {                  if ($revision ne '') {
                 exec($CMD{rlog}, $revision, $fullname);                          exec("rlog",$revision,$fullname);
             }                  }
             else {                  else {
                 exec($CMD{rlog}, $fullname);                          exec("rlog",$fullname);
             }                  }
         }          }
         while (<$fh>) {          while (<$fh>) {
             print if ($verbose);              print if ($verbose);
Line 2074  sub readLog($;$) {
Line 1738  sub readLog($;$) {
 # log info  # log info
 # ----------------------------  # ----------------------------
         logentry:          logentry:
         while (!/$LOG_FILESEPARATOR/o) {          while (!/^=========/) {
             $_ = <$fh>;              $_ = <$fh>;
             last logentry if (!defined($_));    # EOF              last logentry if (!defined($_));    # EOF
             print "R:", $_ if ($verbose);              print "R:", $_ if ($verbose);
Line 2082  sub readLog($;$) {
Line 1746  sub readLog($;$) {
                 $rev = $1;                  $rev = $1;
                 unshift(@allrevisions,$rev);                  unshift(@allrevisions,$rev);
             }              }
             elsif (/$LOG_FILESEPARATOR/o || /$LOG_REVSEPARATOR/o) {              elsif (/^========/ || /^----------------------------$/) {
                 next logentry;                  next logentry;
             }              }
             else {              else {
Line 2116  sub readLog($;$) {
Line 1780  sub readLog($;$) {
             while (<$fh>) {              while (<$fh>) {
                 print "L:", $_ if ($verbose);                  print "L:", $_ if ($verbose);
                 next line if (/^branches:\s/);                  next line if (/^branches:\s/);
                 last line if (/$LOG_FILESEPARATOR/o || /$LOG_REVSEPARATOR/o);                  last line if (/^----------------------------$/ || /^=========/);
                 $log{$rev} .= $_;                  $log{$rev} .= $_;
             }              }
             print "E:", $_ if ($verbose);              print "E:", $_ if ($verbose);
Line 2134  sub readLog($;$) {
Line 1798  sub readLog($;$) {
 # is the first commit listed on the appropriate branch.  # is the first commit listed on the appropriate branch.
 # This is not neccesary the same revision as marked as head in the RCS file.  # This is not neccesary the same revision as marked as head in the RCS file.
         my $headrev = $curbranch || "1";          my $headrev = $curbranch || "1";
         ($symrev{"MAIN"} = $headrev) =~ s/(\d+)$/0.$1/;          ($symrev{"MAIN"} = $headrev) =~ s/(\.?)(\d+)$/${1}0.$2/;
           revision:
         foreach $rev (@revorder) {          foreach $rev (@revorder) {
             if ($rev =~ /^(\S*)\.\d+$/ && $headrev eq $1) {              if ($rev =~ /^(\S*)\.\d+$/ && $headrev eq $1) {
                 $symrev{"HEAD"} = $rev;                  $symrev{"HEAD"} = $rev;
                 last;                  last revision;
             }              }
         }          }
         ($symrev{"HEAD"} = $headrev) =~ s/\.\d+$//          ($symrev{"HEAD"} = $headrev) =~ s/\.\d+$//
Line 2169  sub readLog($;$) {
Line 1834  sub readLog($;$) {
                 # with the branch number 0.A, with the exception that                  # with the branch number 0.A, with the exception that
                 # it has no head to translate to if there is nothing on                  # it has no head to translate to if there is nothing on
                 # the branch, but I guess this can never happen?                  # the branch, but I guess this can never happen?
                 #  
                 # (the code below gracefully forgets about the branch                  # (the code below gracefully forgets about the branch
                 # if it should happen)                  # if it should happen)
                 #                  #
Line 2177  sub readLog($;$) {
Line 1841  sub readLog($;$) {
                 $branch = $3;                  $branch = $3;
                 $branchrev = $head . ($head ne "" ? "." : "") . $branch;                  $branchrev = $head . ($head ne "" ? "." : "") . $branch;
                 my $regex;                  my $regex;
                 $regex = quotemeta $branchrev;                  ($regex = $branchrev) =~ s/\./\\./g;
                 $rev = $head;                  $rev = $head;
   
                   revision:
                 foreach my $r (@revorder) {                  foreach my $r (@revorder) {
                     if ($r =~ /^${regex}\b/) {                      if ($r =~ /^${regex}\b/) {
                         $rev = $branchrev;                          $rev = $branchrev;
                         last;                          last revision;
                     }                      }
                 }                  }
                 next if ($rev eq "");                  next if ($rev eq "");
Line 2239  sub readLog($;$) {
Line 1904  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($;$) {  sub printLog($;$) {
         my ($link, $br, $brp);          my ($link, $br, $brp);
         ($_,$link) = @_;          ($_,$link) = @_;
Line 2281  sub printLog($;$) {
Line 1932  sub printLog($;$) {
             }              }
             print "\n Revision ";              print "\n Revision ";
             &download_link($fileurl, $_, $_,              &download_link($fileurl, $_, $_,
                            $defaultViewable ? "text/x-cvsweb-markup" : undef);                  $defaultViewable ? "text/x-cvsweb-markup" : undef);
             if ($defaultViewable) {              if ($defaultViewable) {
                 print " / (";                  print " / ";
                 &download_link($fileurl, $_, "download", $mimetype);                  &download_link($fileurl, $_, "(download)", $mimetype);
                 print ")";  
             }              }
             if (not $defaultTextPlain) {              if (not $defaultTextPlain) {
                 print " / (";                  print " / ";
                 &download_link($fileurl, $_, "as text", "text/plain");                  &download_link($fileurl, $_, "(as text)",
                 print ")";                             "text/plain");
             }              }
             if (!$defaultViewable) {              if (!$defaultViewable) {
                 print " / (";                  print " / ";
                 &download_link($fileurl, $_, "view", "text/x-cvsweb-markup");                  &download_link($fileurl, $_, "(view)", "text/x-cvsweb-markup");
                 print ")";  
             }              }
             if ($allow_annotate) {              if ($allow_annotate) {
                 print " - ";                  print " - <a href=\"" . $scriptname . "/" . urlencode($where) . "?annotate=$_$barequery\">";
                 print &link('annotate',                  print "annotate</a>";
                             sprintf('%s/%s?annotate=%s%s',  
                                     $scriptname,  
                                     urlencode($where),  
                                     $_,  
                                     $barequery));  
             }              }
             # Plus a select link if enabled, and this version isn't selected              # Plus a select link if enabled, and this version isn't selected
             if ($allow_version_select) {              if ($allow_version_select) {
                 if ((!defined($input{"r1"}) || $input{"r1"} ne $_)) {                  if ((!defined($input{"r1"}) || $input{"r1"} ne $_)) {
                     print " - ";                      print " - <A HREF=\"${scriptwhere}?r1=$_$barequery" .
                     print &link('[select for diffs]',                          "\">[select for diffs]</A>\n";
                                 sprintf('%s?r1=%s%s',  
                                         $scriptwhere,  
                                         $_,  
                                         $barequery));  
                 }                  }
                 else {                  else {
                     print " - <b>[selected]</b>";                      print " - <b>[selected]</b>";
Line 2329  sub printLog($;$) {
Line 1969  sub printLog($;$) {
         }          }
         if (defined @mytz) {          if (defined @mytz) {
             my ($est) = $mytz[(localtime($date{$_}))[8]];              my ($est) = $mytz[(localtime($date{$_}))[8]];
             print ", <i>", scalar localtime($date{$_}), " $est</i> (";              print ", <i>" . scalar localtime($date{$_}) . " $est</i> (";
         } else {          } else {
             print ", <i>", scalar gmtime($date{$_}), " UTC</i> (";              print ", <i>" . scalar gmtime($date{$_}) . " UTC</i> (";
         }          }
         print readableTime(time() - $date{$_},1), " ago)";          print readableTime(time() - $date{$_},1) . " ago)";
         print " by ";          print " by ";
         print "<i>", $author{$_}, "</i>\n";          print "<i>" . $author{$_} . "</i>\n";
         print "<BR>Branch: <b>",$link?link_tags($revsym{$br}):$revsym{$br},"</b>\n"          print "<BR>Branch: <b>",$link?link_tags($revsym{$br}):$revsym{$br},"</b>\n"
             if ($revsym{$br});              if ($revsym{$br});
         print "<BR>CVS Tags: <b>",$link?link_tags($revsym{$_}):$revsym{$_},"</b>"          print "<BR>CVS Tags: <b>",$link?link_tags($revsym{$_}):$revsym{$_},"</b>"
Line 2369  sub printLog($;$) {
Line 2009  sub printLog($;$) {
             # Offer diff to previous revision              # Offer diff to previous revision
             if ($prev) {              if ($prev) {
                 $diffrev{$prev} = 1;                  $diffrev{$prev} = 1;
                   print " to previous <A HREF=\"${scriptwhere}.diff?r1=$prev";
                 my $url = sprintf('%s.diff?r1=%s&r2=%s%s',                  print "&amp;r2=$_" . $barequery . "\">$prev</A>\n";
                                   $scriptwhere,                  if (!$hr_default) { # offer a human readable version if not default
                                   $prev,                      print "(<A HREF=\"${scriptwhere}.diff?r1=$prev";
                                   $_,                      print "&amp;r2=$_" . $barequery . "&amp;f=h\">colored</A>)\n";
                                   $barequery);                  }
   
                 print " to previous ";  
                 printDiffLinks($prev, $url);  
             }              }
             #              #
             # Plus, if it's on a branch, and it's not a vendor branch,              # Plus, if it's on a branch, and it's not a vendor branch,
             # offer a diff with the branch point.              # offer a diff with the branch point.
             if ($revsym{$brp} && !/^1\.1\.1\.\d+$/ && !defined($diffrev{$brp})) {              if ($revsym{$brp} && !/^1\.1\.1\.\d+$/ && !defined($diffrev{$brp})) {
                 my $url = sprintf('%s.diff?r1=%s&r2=%s%s',                  print " to branchpoint <A HREF=\"${scriptwhere}.diff?r1=$brp";
                                   $scriptwhere,                  print "&amp;r2=$_" . $barequery . "\">$brp</A>\n";
                                   $brp,                  if (!$hr_default) { # offer a human readable version if not default
                                   $_,                  print "(<A HREF=\"${scriptwhere}.diff?r1=$brp";
                                   $barequery);                  print "&amp;r2=$_" . $barequery . "&amp;f=h\">colored</A>)\n";
                   }
                 print " to branchpoint ";  
                 printDiffLinks($brp, $url);  
             }              }
             #              #
             # Plus, if it's on a branch, and it's not a vendor branch,              # Plus, if it's on a branch, and it's not a vendor branch,
Line 2400  sub printLog($;$) {
Line 2035  sub printLog($;$) {
             if (/^\d+\.\d+\.\d+/ && !/^1\.1\.1\.\d+$/) {              if (/^\d+\.\d+\.\d+/ && !/^1\.1\.1\.\d+$/) {
                 my ($i,$nextmain);                  my ($i,$nextmain);
                 for ($i = 0; $i < $#revorder && $revorder[$i] ne $_; $i++){}                  for ($i = 0; $i < $#revorder && $revorder[$i] ne $_; $i++){}
                 my @tmp2 = split(/\./, $_);                  my (@tmp2) = split(/\./, $_);
                 for ($nextmain = ""; $i > 0; $i--) {                  for ($nextmain = ""; $i > 0; $i--) {
                     my $next = $revorder[$i-1];                      my ($next) = $revorder[$i-1];
                     my @tmp1 = split(/\./, $next);                      my (@tmp1) = split(/\./, $next);
                     if (@tmp1 < @tmp2) {                      if ($#tmp1 < $#tmp2) {
                         $nextmain = $next;                          $nextmain = $next;
                         last;                          last;
                     }                      }
                     # Only the highest version on a branch should have                      # Only the highest version on a branch should have
                     # a diff for the "next main".                      # a diff for the "next main".
                     last if (@tmp1 - 1 <= @tmp2 &&                      last if (join(".",@tmp1[0..$#tmp1-1])
                              join(".",@tmp1[0..$#tmp1-1]) eq join(".",@tmp2[0..$#tmp1-1]));                               eq join(".",@tmp2[0..$#tmp1-1]));
                 }                  }
                 if (!defined($diffrev{$nextmain})) {                  if (!defined($diffrev{$nextmain})) {
                     $diffrev{$nextmain} = 1;                      $diffrev{$nextmain} = 1;
                       print " next main <A HREF=\"${scriptwhere}.diff?r1=$nextmain";
                     my $url = sprintf('%s.diff?r1=%s&r2=%s%s',                      print "&amp;r2=$_" . $barequery .
                                       $scriptwhere,                          "\">$nextmain</A>\n";
                                       $nextmain,                      if (!$hr_default) { # offer a human readable version if not default
                                       $_,                          print "(<A HREF=\"${scriptwhere}.diff?r1=$nextmain";
                                       $barequery);                          print "&amp;r2=$_" . $barequery .
                               "&amp;f=h\">colored</A>)\n";
                     print " next main ";                      }
                     printDiffLinks($nextmain, $url);  
                 }                  }
             }              }
             # Plus if user has selected only r1, then present a link              # Plus if user has selected only r1, then present a link
             # to make a diff to that revision              # to make a diff to that revision
             if (defined($input{"r1"}) && !defined($diffrev{$input{"r1"}})) {              if (defined($input{"r1"}) && !defined($diffrev{$input{"r1"}})) {
                 $diffrev{$input{"r1"}} = 1;                  $diffrev{$input{"r1"}} = 1;
                   print " to selected <A HREF=\"${scriptwhere}.diff?"
                           . "r1=$input{'r1'}&amp;r2=$_" . $barequery
                           . "\">$input{'r1'}</A>\n";
                   if (!$hr_default) { # offer a human readable version if not default
                       print "(<A HREF=\"${scriptwhere}.diff?r1=$input{'r1'}";
                       print "&amp;r2=$_" . $barequery .
                           "&amp;f=h\">colored</A>)\n";
   
                 my $url = sprintf('%s.diff?r1=%s&r2=%s%s',                  }
                                   $scriptwhere,  
                                   $input{'r1'},  
                                   $_,  
                                   $barequery);  
   
                 print " to selected ";  
                 printDiffLinks($input{'r1'}, $url);  
             }              }
         }          }
         print "<PRE>\n";          print "<PRE>\n";
Line 2457  sub doLog($) {
Line 2091  sub doLog($) {
         ($filename = $where) =~ s|^.*/||;          ($filename = $where) =~ s|^.*/||;
         $backurl = $scriptname . "/" . urlencode($upwhere) . $query;          $backurl = $scriptname . "/" . urlencode($upwhere) . $query;
         print &link($backicon, "$backurl#$filename"),          print &link($backicon, "$backurl#$filename"),
           " <b>Up to ", &clickablePath($upwhere, 1), "</b><p>\n";                " <b>Up to ", &clickablePath($upwhere, 1), "</b><p>\n";
         print &link('Request diff between arbitrary revisions', '#diff');          print <<EOF;
         print '<HR NOSHADE>';  <A HREF="#diff">Request diff between arbitrary revisions</A>
   <HR NOSHADE>
   EOF
         if ($curbranch) {          if ($curbranch) {
             print "Default branch: ", ($revsym{$curbranch} || $curbranch);              print "Default branch: ", ($revsym{$curbranch} || $curbranch);
         }          }
Line 2488  sub doLog($) {
Line 2123  sub doLog($) {
         print "</A><P>\n";          print "</A><P>\n";
         print "<FORM METHOD=\"GET\" ACTION=\"${scriptwhere}.diff\" NAME=\"diff_select\">\n";          print "<FORM METHOD=\"GET\" ACTION=\"${scriptwhere}.diff\" NAME=\"diff_select\">\n";
         foreach (@stickyvars) {          foreach (@stickyvars) {
             printf('<INPUT TYPE=HIDDEN NAME="%s" VALUE="%s">',  $_, $input{$_})              print "<INPUT TYPE=HIDDEN NAME=\"$_\" VALUE=\"$input{$_}\">\n"
                 if (defined($input{$_})                  if (defined($input{$_})
                     && ((!defined($DEFAULTVALUE{$_})                      && ($input{$_} ne $DEFAULTVALUE{$_} && $input{$_} ne ""));
                          || $input{$_} ne $DEFAULTVALUE{$_})  
                         && $input{$_} ne ""));  
         }          }
         print "<TABLE><TR>\n";          print "Diffs between \n";
         print "<TD align=right>Diffs between \n";  
         print "<SELECT NAME=\"r1\">\n";          print "<SELECT NAME=\"r1\">\n";
         print "<OPTION VALUE=\"text\" SELECTED>Use Text Field\n";          print "<OPTION VALUE=\"text\" SELECTED>Use Text Field\n";
         print $sel;          print $sel;
         print "</SELECT>\n";          print "</SELECT>\n";
         $diffrev = $revdisplayorder[$#revdisplayorder];          $diffrev = $revdisplayorder[$#revdisplayorder];
         $diffrev = $input{"r1"} if (defined($input{"r1"}));          $diffrev = $input{"r1"} if (defined($input{"r1"}));
         print "<INPUT TYPE=\"TEXT\" SIZE=\"$inputTextSize\" NAME=\"tr1\" VALUE=\"$diffrev\" onChange='document.diff_select.r1.selectedIndex=0'></TD>";          print "<INPUT TYPE=\"TEXT\" SIZE=\"$inputTextSize\" NAME=\"tr1\" VALUE=\"$diffrev\" onChange='document.diff_select.r1.selectedIndex=0'>\n";
         print "<TD><BR></TD></TR>\n";          print " and \n";
         print "<TR><TD align=right>and \n";  
         print "<SELECT NAME=\"r2\">\n";          print "<SELECT NAME=\"r2\">\n";
         print "<OPTION VALUE=\"text\" SELECTED>Use Text Field\n";          print "<OPTION VALUE=\"text\" SELECTED>Use Text Field\n";
         print $sel;          print $sel;
         print "</SELECT>\n";          print "</SELECT>\n";
         $diffrev = $revdisplayorder[0];          $diffrev = $revdisplayorder[0];
         $diffrev = $input{"r2"} if (defined($input{"r2"}));          $diffrev = $input{"r2"} if (defined($input{"r2"}));
         print "<INPUT TYPE=\"TEXT\" SIZE=\"$inputTextSize\" NAME=\"tr2\" VALUE=\"$diffrev\" onChange='document.diff_select.r2.selectedIndex=0'></TD>";          print "<INPUT TYPE=\"TEXT\" SIZE=\"$inputTextSize\" NAME=\"tr2\" VALUE=\"$diffrev\" onChange='document.diff_select.r2.selectedIndex=0'>\n";
         print "<TD><INPUT TYPE=SUBMIT VALUE=\"  Get Diffs  \"></TD>\n";          print "<BR>Type of Diff should be a&nbsp;";
           printDiffSelect(0);
           print "<INPUT TYPE=SUBMIT VALUE=\"  Get Diffs  \">\n";
         print "</FORM>\n";          print "</FORM>\n";
         print "</TR></TABLE>\n";  
         print "<HR noshade>\n";          print "<HR noshade>\n";
         print "<TABLE>";  
         print "<FORM METHOD=\"GET\" ACTION=\"$scriptwhere\">\n";  
         print "<TR><TD align=right>Preferred Diff type:</TD>";  
         print "<TD>";  
         printDiffSelect($use_java_script);  
         print "</TD><TD></TD></TR>\n";  
         if (@branchnames) {          if (@branchnames) {
             print "<TR><TD align=right>View only Branch:</TD>";  
             print "<TD>";  
             print "<A name=branch></A>\n";              print "<A name=branch></A>\n";
               print "<FORM METHOD=\"GET\" ACTION=\"$scriptwhere\">\n";
               foreach (@stickyvars) {
                   next if ($_ eq "only_with_tag");
                   next if ($_ eq "logsort");
                   print "<INPUT TYPE=HIDDEN NAME=\"$_\" VALUE=\"$input{$_}\">\n"
                       if (defined($input{$_}) && $input{$_} ne $DEFAULTVALUE{$_}
                           && $input{$_} ne "");
               }
               print "View only Branch: \n";
             print "<SELECT NAME=\"only_with_tag\"";              print "<SELECT NAME=\"only_with_tag\"";
             print " onchange=\"submit()\"" if ($use_java_script);              print " onchange=\"submit()\"" if ($use_java_script);
             print ">\n";              print ">\n";
Line 2539  sub doLog($) {
Line 2172  sub doLog($) {
                         && $input{"only_with_tag"} eq $_);                          && $input{"only_with_tag"} eq $_);
                 print ">${_}\n";                  print ">${_}\n";
             }              }
             print "</SELECT></TD><TD></TD></TR>\n";              print "</SELECT>\n";
               print "<INPUT TYPE=SUBMIT VALUE=\"  View Branch  \">\n";
               print "</FORM>\n";
         }          }
           print "<A name=logsort></A>\n";
           print "<FORM METHOD=\"GET\" ACTION=\"$scriptwhere\">\n";
         foreach (@stickyvars) {          foreach (@stickyvars) {
             next if ($_ eq "f");  
             next if ($_ eq "only_with_tag");              next if ($_ eq "only_with_tag");
             next if ($_ eq "logsort");              next if ($_ eq "logsort");
             print "<INPUT TYPE=HIDDEN NAME=\"$_\" VALUE=\"$input{$_}\">\n"              print "<INPUT TYPE=HIDDEN NAME=\"$_\" VALUE=\"$input{$_}\">\n"
                 if (defined($input{$_})                  if (defined($input{$_}) && $input{$_} ne $DEFAULTVALUE{$_}
                     && (!defined($DEFAULTVALUE{$_})  
                         || $input{$_} ne $DEFAULTVALUE{$_})  
                     && $input{$_} ne "");                      && $input{$_} ne "");
         }          }
         print "<TR><TD align=right>";          print "Sort log by: \n";
         print "<A name=logsort></A>\n";          print "<SELECT NAME=\"logsort\"";
         print "Sort log by:</TD>";          print " onchange=\"submit()\"" if ($use_java_script);
         print "<TD>";          print ">\n";
         printLogSortSelect($use_java_script);          print "<OPTION VALUE=cvs",$logsort eq "cvs" ? " SELECTED" : "", ">Not sorted";
         print "</TD>";          print "<OPTION VALUE=date",$logsort eq "date" ? " SELECTED" : "", ">Commit date";
         print "<TD><INPUT TYPE=SUBMIT VALUE=\"  Set  \"></TD>";          print "<OPTION VALUE=rev",$logsort eq "rev" ? " SELECTED" : "", ">Revision";
           print "</SELECT>\n";
           print "<INPUT TYPE=SUBMIT VALUE=\"  Sort  \">\n";
         print "</FORM>\n";          print "</FORM>\n";
         print "</TR></TABLE>";  
         print &html_footer;          print &html_footer;
         print "</BODY></HTML>\n";          print "</BODY></HTML>\n";
 }  }
Line 2599  sub flush_diff_rows($$$$) {
Line 2234  sub flush_diff_rows($$$$) {
 # human_readable_diff(String revision_to_return_to);  # human_readable_diff(String revision_to_return_to);
 ##  ##
 sub human_readable_diff($){  sub human_readable_diff($){
   my ($difftxt, $where_nd, $filename, $pathname, $scriptwhere_nd);    my ($i,$difftxt, $where_nd, $filename, $pathname, $scriptwhere_nd);
   my ($fh, $rev) = @_;    my ($fh, $rev) = @_;
   my ($date1, $date2, $r1d, $r2d, $r1r, $r2r, $rev1, $rev2, $sym1, $sym2);    my ($date1, $date2, $r1d, $r2d, $r1r, $r2r, $rev1, $rev2, $sym1, $sym2);
   my (@rightCol, @leftCol);    my (@rightCol, @leftCol);
Line 2626  sub human_readable_diff($){
Line 2261  sub human_readable_diff($){
     $date2 = $r2d;      $date2 = $r2d;
   }    }
   
   print "<h3 align=center>Diff for /$where_nd between version $rev1 and $rev2</h3>\n",    print "<h3 align=center>Diff for /$where_nd between version $rev1 and $rev2</h3>\n";
     "<table border=0 cellspacing=0 cellpadding=0 width=\"100%\">\n",  
       "<tr bgcolor=\"#ffffff\">\n",    print "<table border=0 cellspacing=0 cellpadding=0 width=\"100%\">\n";
         "<th width=\"50%\" valign=TOP>",    print "<tr bgcolor=\"#ffffff\">\n";
           "version $rev1";    print "<th width=\"50%\" valign=TOP>";
     print "version $rev1";
   print ", $date1" if (defined($date1));    print ", $date1" if (defined($date1));
   print "<br>Tag: $sym1\n" if ($sym1);    print "<br>Tag: $sym1\n" if ($sym1);
   print "</th>\n",    print "</th>\n";
     "<th width=\"50%\" valign=TOP>",    print "<th width=\"50%\" valign=TOP>";
       "version $rev2";    print "version $rev2";
   print ", $date2" if (defined($date2));    print ", $date2" if (defined($date2));
   print "<br>Tag: $sym2\n" if ($sym1);    print "<br>Tag: $sym2\n" if ($sym1);
   print "</th>\n";    print "</th>\n";
Line 2652  sub human_readable_diff($){
Line 2288  sub human_readable_diff($){
   # cascading style sheets because we've to set the    # cascading style sheets because we've to set the
   # font and color for each row. anyone ...?    # font and color for each row. anyone ...?
   ####    ####
     while (<$fh>) {
         $difftxt = $_;
   
   # prefetch several lines  
   my @buf = head($fh);  
   
   my %d = scan_directives(@buf);  
   
   while (@buf || !eof($fh)) {  
       $difftxt = @buf ? shift @buf : <$fh>;  
   
       if ($difftxt =~ /^@@/) {        if ($difftxt =~ /^@@/) {
           ($oldline,$newline,$funname) = $difftxt =~ /@@ \-([0-9]+).*\+([0-9]+).*@@(.*)/;            ($oldline,$newline,$funname) = $difftxt =~ /@@ \-([0-9]+).*\+([0-9]+).*@@(.*)/;
           print  "<tr bgcolor=\"$diffcolorHeading\"><td width=\"50%\">";            print  "<tr bgcolor=\"$diffcolorHeading\"><td width=\"50%\">";
Line 2676  sub human_readable_diff($){
Line 2306  sub human_readable_diff($){
       }        }
       else {        else {
           ($diffcode,$rest) = $difftxt =~ /^([-+ ])(.*)/;            ($diffcode,$rest) = $difftxt =~ /^([-+ ])(.*)/;
           $_ = spacedHtmlText($rest, $d{'tabstop'});            $_ = spacedHtmlText ($rest);
   
           # Add fontface, size            # Add fontface, size
           $_ = "$fs&nbsp;$_$fe";            $_ = "$fs&nbsp;$_$fe";
Line 2758  sub navigateHeader($$$$$) {
Line 2388  sub navigateHeader($$$$$) {
     my ($swhere,$path,$filename,$rev,$title) = @_;      my ($swhere,$path,$filename,$rev,$title) = @_;
     $swhere = "" if ($swhere eq $scriptwhere);      $swhere = "" if ($swhere eq $scriptwhere);
     $swhere = urlencode($filename) if ($swhere eq "");      $swhere = urlencode($filename) if ($swhere eq "");
       print "<\!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">";
     print <<EOF;      print "<HTML>\n<HEAD>\n";
 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">      print '<!-- CVSweb $zRevision: 1.94 $  $Revision$ -->';
 <HTML>      print "\n<TITLE>$path$filename - $title - $rev</TITLE></HEAD>\n";
 <HEAD>      print  "<BODY BGCOLOR=\"$backcolor\">\n";
 <META name="robots" content="nofollow">      print "<table width=\"100%\" border=0 cellspacing=0 cellpadding=1 bgcolor=\"$navigationHeaderColor\">";
 <!-- knu-cvsweb $cvsweb_revision -->      print "<tr valign=bottom><td>";
 <TITLE>$path$filename - $title - $rev</TITLE></HEAD>      print  "<a href=\"$swhere$query#rev$rev\">$backicon";
 $body_tag_for_src  
 <table width="100%" border=0 cellspacing=0 cellpadding=1 bgcolor="$navigationHeaderColor">  
 <tr valign=bottom><td>  
 EOF  
   
     print &link($backicon, "$swhere$query#rev$rev");  
     print "</a> <b>Return to ", &link("$filename","$swhere$query#rev$rev")," CVS log";      print "</a> <b>Return to ", &link("$filename","$swhere$query#rev$rev")," CVS log";
     print "</b> $fileicon</td>";      print "</b> $fileicon</td>";
   
Line 2782  EOF
Line 2406  EOF
 sub plural_write($$) {  sub plural_write($$) {
     my ($num,$text) = @_;      my ($num,$text) = @_;
     if ($num != 1) {      if ($num != 1) {
         $text .= "s";          $text = $text . "s";
     }      }
     if ($num > 0) {      if ($num > 0) {
         return join(' ', $num, $text);          return $num . " " . $text;
     }      }
     else {      else {
         return "";          return "";
Line 2829  sub readableTime($$) {
Line 2453  sub readableTime($$) {
         my $resttime = plural_write(int ($rest / $break),          my $resttime = plural_write(int ($rest / $break),
                                 $desc{$break});                                  $desc{$break});
         if ($resttime) {          if ($resttime) {
             $retval .= ", $resttime";              $retval = $retval . ", " . $resttime;
         }          }
     }      }
   
Line 2853  sub clickablePath($$) {
Line 2477  sub clickablePath($$) {
         $retval = "[$cvstree]";          $retval = "[$cvstree]";
     }      }
     else {      else {
         $retval .= ' ' . &link("[$cvstree]", sprintf('%s/%s#dirlist',          $retval = $retval . " <a href=\"${scriptname}/${query}#dirlist\">[$cvstree]</a>";
                                                      $scriptname,  
                                                      $query));  
         my $wherepath = '';          my $wherepath = '';
         my ($lastslash) = $pathname =~ m|/$|;          my ($lastslash) = $pathname =~ m|/$|;
         foreach (split(/\//, $pathname)) {          foreach (split(/\//, $pathname)) {
             $retval .= " / ";              $retval = $retval . " / ";
             $wherepath .= "/$_";              $wherepath = $wherepath . '/' . $_;
             my ($last) = "$wherepath/" eq "/$pathname"              my ($last) = "$wherepath/" eq "/$pathname"
                 || $wherepath eq "/$pathname";                  || $wherepath eq "/$pathname";
             if ($clickLast || !$last) {              if ($clickLast || !$last) {
                 $retval .= &link($_, join('',                  $retval = $retval . "<a href=\"${scriptname}"
                                           $scriptname,                      . urlencode($wherepath)
                                           urlencode($wherepath),                      . (!$last || $lastslash ? '/' : '')
                                           (!$last || $lastslash ? '/' : ''),                      . ${query}
                                           $query,                      . (!$last || $lastslash ? "#dirlist" : "")
                                           (!$last || $lastslash ? "#dirlist" : "")));                      . "\">$_</a>";
             }              }
             else { # do not make a link to the current dir              else { # do not make a link to the current dir
                 $retval .= $_;                  $retval = $retval .  $_;
             }              }
         }          }
     }      }
Line 2880  sub clickablePath($$) {
Line 2502  sub clickablePath($$) {
 }  }
   
 sub chooseCVSRoot() {  sub chooseCVSRoot() {
     if (2 <= @CVSROOT) {      my @foo;
       foreach (sort keys %CVSROOT) {
           if (-d $CVSROOT{$_}) {
               push(@foo, $_);
           }
       }
       if (@foo > 1) {
         my ($k);          my ($k);
         print "<form method=\"GET\" action=\"${scriptwhere}\">\n";          print "<form method=\"GET\" action=\"${scriptwhere}\">\n";
         foreach $k (keys %input) {          foreach $k (keys %input) {
Line 2895  sub chooseCVSRoot() {
Line 2523  sub chooseCVSRoot() {
         print "<td>\n<select name=\"cvsroot\"";          print "<td>\n<select name=\"cvsroot\"";
         print " onchange=\"submit()\"" if ($use_java_script);          print " onchange=\"submit()\"" if ($use_java_script);
         print ">\n";          print ">\n";
         foreach $k (@CVSROOT) {          foreach $k (@foo) {
             print "<option value=\"$k\"";              print "<option value=\"$k\"";
             print " selected" if ($k eq $cvstree);              print " selected" if ($k eq $cvstree);
             print ">", ($CVSROOTdescr{$k} ? $CVSROOTdescr{$k} : $k), "</option>\n";              print ">" . ($CVSROOTdescr{$k} ? $CVSROOTdescr{$k} :
                           $k). "</option>\n";
         }          }
         print "</select>\n</td>";          print "</select>\n</td>";
         print "<td>";          print "<td><input type=submit value=\"Go\"></td>";
           print "</tr></table></form>";
     }      }
     else {      else {
         # no choice ..          # no choice ..
         print "CVS Root: <b>[$cvstree]</b>";          print "CVS Root: <b>[$cvstree]</b>";
     }      }
   
     print " Module path or alias:\n";  
     print "<INPUT TYPE=TEXT NAME=\"path\" VALUE=\"\" SIZE=15>\n";  
     print "<input type=submit value=\"Go\">";  
   
     if (2 <= @CVSROOT) {  
         print "</td></tr></table></form>";  
     }  
 }  }
   
 sub chooseMirror() {  sub chooseMirror() {
Line 2929  sub chooseMirror() {
Line 2551  sub chooseMirror() {
         print "\nThis cvsweb is mirrored in:\n";          print "\nThis cvsweb is mirrored in:\n";
         foreach $mirror (keys %MIRRORS) {          foreach $mirror (keys %MIRRORS) {
             print ", " if ($moremirrors);              print ", " if ($moremirrors);
             print &link(htmlquote($mirror),$MIRRORS{$mirror});              print qq(<a href="$MIRRORS{$mirror}">$mirror</A>\n);
             $moremirrors = 1;              $moremirrors = 1;
         }          }
         print "<p>\n";          print "<p>\n";
Line 2972  sub download_url($$;$) {
Line 2594  sub download_url($$;$) {
   
     $revision =~ s/\b0\.//;      $revision =~ s/\b0\.//;
   
     if (defined($checkoutMagic)      if (defined($checkout_magic)
         && (!defined($mimetype) || $mimetype ne "text/x-cvsweb-markup")) {          && (!defined($mimetype) || $mimetype ne "text/x-cvsweb-markup")) {
         my $path = $where;          my ($path);
         $path =~ s|/[^/]*$|/|;          ($path = $where) =~ s|/[^/]*$|/|;
         $url = "$scriptname/$checkoutMagic/${path}$url";          $url = "$scriptname/$checkoutMagic/${path}$url";
     }      }
     $url .= "?rev=$revision";      $url .= "?rev=$revision";
     $url .= '&content-type=' . urlencode($mimetype) if (defined($mimetype));      $url .= "&amp;content-type=$mimetype" if (defined($mimetype));
   
     $url;      return $url;
 }  }
   
 # Presents a link to download the  # Presents a link to download the
 # selected revision  # selected revision
 sub download_link($$$;$) {  sub download_link($$$;$) {
     my ($url, $revision, $textlink, $mimetype) = @_;      my ($url,$revision,$textlink,$mimetype) = @_;
     my ($fullurl) = download_url($url, $revision, $mimetype);      my ($fullurl) = download_url($url,$revision,$mimetype);
       my ($paren) = $textlink =~ /^\(/;
     printf '<A HREF="%s"', hrefquote("$fullurl$barequery");      $textlink =~ s/^\(// if ($paren);
       $textlink =~ s/\)$// if ($paren);
       print "(" if ($paren);
       print "<A HREF=\"$fullurl";
       print $barequery;
       print "\"";
     if ($open_extern_window && (!defined($mimetype) || $mimetype ne "text/x-cvsweb-markup")) {      if ($open_extern_window && (!defined($mimetype) || $mimetype ne "text/x-cvsweb-markup")) {
         print ' target="cvs_checkout"';          print " target=\"cvs_checkout\"";
         # we should have          # we should have
         #   'if (document.cvswin==null) document.cvswin=window.open(...'          #   'if (document.cvswin==null) document.cvswin=window.open(...'
         # in order to allow the user to resize the window; otherwise          # in order to allow the user to resize the window; otherwise
Line 3012  sub download_link($$$;$) {
Line 2638  sub download_link($$$;$) {
         # currently, the best way is to comment out the size parameters          # currently, the best way is to comment out the size parameters
         # ($extern_window...) in cvsweb.conf.          # ($extern_window...) in cvsweb.conf.
         if ($use_java_script) {          if ($use_java_script) {
             my @attr = qw(resizeable scrollbars);              print " onClick=\"window.open('$fullurl','cvs_checkout',";
               print "'resizeable,scrollbars";
             push @attr, qw(status toolbar)              print ",status,toolbar" if (defined($mimetype)
               if (defined($mimetype) && $mimetype eq "text/html");                  && $mimetype eq "text/html");
               print ",width=$extern_window_width" if (defined($extern_window_width));
             push @attr, "width=$extern_window_width"              print ",height=$extern_window_height" if (defined($extern_window_height));
               if (defined($extern_window_width));              print"');\"";
   
             push @attr, "height=$extern_window_height"  
               if (defined($extern_window_height));  
   
             printf q` onClick="window.open('%s','cvs_checkout','%s');"`,  
               hrefquote($fullurl), join(',', @attr);  
         }          }
     }      }
     print "><b>$textlink</b></A>";      print "><b>$textlink</b></A>";
       print ")" if ($paren);
 }  }
   
 # Returns a Query string with the  # Returns a Query string with the
Line 3049  sub toggleQuery($$) {
Line 2670  sub toggleQuery($$) {
         my ($value) = defined($vars{$var}) ? $vars{$var} : "";          my ($value) = defined($vars{$var}) ? $vars{$var} : "";
         my ($default) = defined($DEFAULTVALUE{$var}) ? $DEFAULTVALUE{$var} : "";          my ($default) = defined($DEFAULTVALUE{$var}) ? $DEFAULTVALUE{$var} : "";
         if ($value ne $default) {          if ($value ne $default) {
             $newquery .= "&" if ($newquery ne "");              $newquery .= "&amp;" if ($newquery ne "");
             $newquery .= urlencode($var) . "=" . urlencode($value);              $newquery .= urlencode($var) . "=" . urlencode($value);
         }          }
     }      }
Line 3060  sub toggleQuery($$) {
Line 2681  sub toggleQuery($$) {
 }  }
   
 sub urlencode($) {  sub urlencode($) {
     local($_) = @_;      my ($in) = @_;
       my ($out);
     s/[\000-+{-\377]/sprintf("%%%02x", ord($&))/ge;      ($out = $in) =~ s/([\000-+{-\377])/sprintf("%%%02x", ord($1))/ge;
       return $out;
     $_;  
 }  }
   
 sub htmlquote($) {  
     local($_) = @_;  
   
     # Special Characters; RFC 1866  
     s/&/&amp;/g;  
     s/\"/&quot;/g;  
     s/</&lt;/g;  
     s/>/&gt;/g;  
   
     $_;  
 }  
   
 sub htmlunquote($) {  
     local($_) = @_;  
   
     # Special Characters; RFC 1866  
     s/&quot;/\"/g;  
     s/&lt;/</g;  
     s/&gt;/>/g;  
     s/&amp;/&/g;  
   
     $_;  
 }  
   
 sub hrefquote($) {  
     local($_) = @_;  
   
     y/ /+/;  
   
     htmlquote($_)  
 }  
   
 sub http_header(;$) {  sub http_header(;$) {
     my $content_type = shift || "text/html";      my $content_type = shift || "text/html";
   
     $content_type .= "; charset=$charset"  
       if $content_type =~ m,^text/, && defined($charset) && $charset;  
   
     if (defined($moddate)) {      if (defined($moddate)) {
         if ($is_mod_perl) {          if ($is_mod_perl) {
             Apache->request->header_out("Last-Modified" => scalar gmtime($moddate) . " GMT");              Apache->request->header_out(Last_modified => scalar gmtime($moddate) . " GMT");
         }          }
         else {          else {
             print "Last-Modified: ", scalar gmtime($moddate), " GMT\r\n";              print "Last-Modified: " . scalar gmtime($moddate) . " GMT\r\n";
         }          }
     }      }
     if ($is_mod_perl) {      if ($is_mod_perl) {
Line 3120  sub http_header(;$) {
Line 2704  sub http_header(;$) {
             print "Content-type: $content_type\r\n";              print "Content-type: $content_type\r\n";
     }      }
     if ($allow_compress && $maycompress) {      if ($allow_compress && $maycompress) {
         if ($has_zlib || (defined($CMD{gzip}) && open(GZIP, "| $CMD{gzip} -1 -c"))) {          my $fh = do {local(*FH);};
           if (defined($GZIPBIN) && open($fh, "|$GZIPBIN -1 -c")) {
             if ($is_mod_perl) {              if ($is_mod_perl) {
                     Apache->request->content_encoding("x-gzip");                      Apache->request->content_encoding("x-gzip");
                     Apache->request->header_out(Vary => "Accept-Encoding");                      Apache->request->header_out(Vary => "Accept-Encoding");
Line 3132  sub http_header(;$) {
Line 2717  sub http_header(;$) {
                     print "\r\n"; # Close headers                      print "\r\n"; # Close headers
             }              }
             $| = 1; $| = 0; # Flush header output              $| = 1; $| = 0; # Flush header output
             if ($has_zlib) {              select ($fh);
                 tie *GZIP, __PACKAGE__, \*STDOUT;  #           print "<!-- gzipped -->" if ($content_type eq "text/html");
             }  
             select(GZIP);  
             $gzip_open = 1;  
 #           print "<!-- gzipped -->" if ($content_type =~ m|^text/html\b|);  
         }          }
         else {          else {
             if ($is_mod_perl) {              if ($is_mod_perl) {
Line 3146  sub http_header(;$) {
Line 2727  sub http_header(;$) {
             else {              else {
                     print "\r\n"; # Close headers                      print "\r\n"; # Close headers
             }              }
             print "<font size=-1>Unable to find gzip binary in the <b>\$command_path</b> ($command_path) to compress output</font><br>";              print "<font size=-1>Unable to find gzip binary in the \$PATH to compress output</font><br>";
         }          }
     }      }
     else {      else {
Line 3161  sub http_header(;$) {
Line 2742  sub http_header(;$) {
   
 sub html_header($) {  sub html_header($) {
     my ($title) = @_;      my ($title) = @_;
     http_header("text/html");      my $version = '$zRevision: 1.94 $  $Revision$'; #'
       http_header();
     print <<EOH;      print <<EOH;
 <!doctype html public "-//W3C//DTD HTML 4.0 Transitional//EN"  <!doctype html public "-//W3C//DTD HTML 4.0 Transitional//EN"
  "http://www.w3.org/TR/REC-html40/loose.dtd">   "http://www.w3.org/TR/REC-html40/loose.dtd">
 <html>  <html>
 <head>  
 <meta name="robots" content="nofollow">  
 <title>$title</title>  <title>$title</title>
 <!-- knu-cvsweb $cvsweb_revision -->  <!-- CVSweb $version -->
 </head>  </head>
 $body_tag  $body_tag
 $logo <h1 align="center">$title</h1>  $logo <h1 align="center">$title</h1>
Line 3190  sub link_tags($) {
Line 2770  sub link_tags($) {
   
     foreach my $sym (split(", ", $tags)) {      foreach my $sym (split(", ", $tags)) {
         $ret .= ",\n" if ($ret ne "");          $ret .= ",\n" if ($ret ne "");
         $ret .= &link($sym, $fileurl . toggleQuery('only_with_tag',$sym));          $ret .= "<A HREF=\"$fileurl"
                   . toggleQuery('only_with_tag',$sym) . "\">$sym</A>";
     }      }
     return "$ret\n";      return $ret."\n";
 }  }
   
 #  #
Line 3206  sub forbidden_module($) {
Line 2787  sub forbidden_module($) {
     }      }
   
     return 0;      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  
 # library.  
   
 sub MAGIC1() { 0x1f }  
 sub MAGIC2() { 0x8b }  
 sub OSCODE() { 3    }  
   
 sub TIEHANDLE {  
         my ($class, $out) = @_;  
         my ($d) = Compress::Zlib::deflateInit(-Level => Compress::Zlib::Z_BEST_COMPRESSION(),  
                 -WindowBits => -Compress::Zlib::MAX_WBITS()) or return undef;  
         my ($o) = {  
                 handle => $out,  
                 dh => $d,  
                 crc => 0,  
                 len => 0,  
         };  
         my ($header) = pack("c10", MAGIC1, MAGIC2, Compress::Zlib::Z_DEFLATED(), 0,0,0,0,0,0, OSCODE);  
         print {$o->{handle}} $header;  
         return bless($o, $class);  
 }  
   
 sub PRINT {  
         my ($o) = shift;  
         my ($buf) = join(defined $, ? $, : "",@_);  
         my ($len) = length($buf);  
         my ($compressed, $status) = $o->{dh}->deflate($buf);  
         print {$o->{handle}} $compressed if defined($compressed);  
         $o->{crc} = Compress::Zlib::crc32($buf, $o->{crc});  
         $o->{len} += $len;  
         return $len;  
 }  
   
 sub PRINTF {  
         my ($o) = shift;  
         my ($fmt) = shift;  
         my ($buf) = sprintf($fmt, @_);  
         my ($len) = length($buf);  
         my ($compressed, $status) = $o->{dh}->deflate($buf);  
         print {$o->{handle}} $compressed if defined($compressed);  
         $o->{crc} = Compress::Zlib::crc32($buf, $o->{crc});  
         $o->{len} += $len;  
         return $len;  
 }  
   
 sub WRITE {  
         my ($o, $buf, $len, $off) = @_;  
         my ($compressed, $status) = $o->{dh}->deflate(substr($buf, 0, $len));  
         print {$o->{handle}} $compressed if defined($compressed);  
         $o->{crc} = Compress::Zlib::crc32(substr($buf, 0, $len), $o->{crc});  
         $o->{len} += $len;  
         return $len;  
 }  
   
 sub CLOSE {  
         my ($o) = @_;  
         return if !defined( $o->{dh});  
         my ($buf) = $o->{dh}->flush();  
         $buf .= pack("V V", $o->{crc}, $o->{len});  
         print {$o->{handle}} $buf;  
         undef $o->{dh};  
 }  
   
 sub DESTROY {  
         my ($o) = @_;  
         CLOSE($o);  
 }  }

Legend:
Removed from v.1.1.1.22  
changed lines
  Added in v.3.18

CVSweb