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

Diff for /cvsweb/cvsweb.cgi between version 3.9 and 3.37

version 3.9, 2000/07/29 12:41:25 version 3.37, 2000/10/20 15:46:01
Line 1 
Line 1 
 #!/usr/bin/perl -ws  #!/usr/bin/perl5 -ws
 #  #
 # cvsweb - a CGI interface to CVS trees.  # cvsweb - a CGI interface to CVS trees.
 #  #
 # Written in their spare time by  # Written in their spare time by
 #             Bill Fenner      <fenner@FreeBSD.org>   (original work)  #             Bill Fenner      <fenner@FreeBSD.org>   (original work)
 # extended by Henner Zeller    <zeller@think.de>,  # extended by Henner Zeller    <zeller@think.de>,
 #             Henrik Nordstrom <hno@hem.passagen.se>  #             Henrik Nordstrom <hno@hem.passagen.se>
 #             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 41 
Line 42 
 # 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.93 2000/07/27 17:42:28 hzeller Exp $  # $zId: cvsweb.cgi,v 1.103 2000/09/20 17:02:29 jumager Exp $
 # $Id$  # $Id$
 #  #
 ###  ###
Line 51  use strict;
Line 52  use strict;
 use vars qw (  use vars qw (
     $config $allow_version_select $verbose      $config $allow_version_select $verbose
     %CVSROOT %CVSROOTdescr %MIRRORS %DEFAULTVALUE %ICONS %MTYPES      %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 $prcgi      @revisions %state %difflines %log %branchpoint @revorder
     @prcategories $prcategories      $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_lynx $is_w3m $is_msie $is_mozilla3 $is_textbased      $is_links $is_lynx $is_w3m $is_msie $is_mozilla3 $is_textbased
     %input $query $barequery $sortby $bydate $byrev $byauthor      %input $query $barequery $sortby $bydate $byrev $byauthor
     $bylog $byfile $hr_default $logsort $cvstree $cvsroot      $bylog $byfile $defaultDiffType $logsort $cvstree $cvsroot
     $mimetype $defaultTextPlain $defaultViewable $allow_compress      $mimetype $defaultTextPlain $defaultViewable $allow_compress
     $GZIPBIN $backicon $diricon $fileicon $fullname $newname      $GZIPBIN $backicon $diricon $fileicon $fullname $newname
     $cvstreedefault $body_tag $logo $defaulttitle $address      $cvstreedefault $body_tag $body_tag_for_src
     $backcolor $long_intro $short_instruction $shortLogLen      $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 72  use vars qw (
Line 75  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
     $checkout_magic $show_subdir_lastmod $show_log_in_markup $v      $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      $use_moddate $has_zlib $gzip_open
       $LOG_FILESEPARATOR $LOG_REVSEPARATOR
 );  );
   
   sub printDiffSelect($);
   sub printDiffLinks($$);
   sub printLogSortSelect($);
   sub findLastModifiedSubdirs(@);
   sub htmlify_sub(&$);
   sub htmlify($;$);
   sub spacedHtmlText($;$);
   sub link($$);
   sub revcmp($$);
   sub fatal($$);
   sub redirect($);
   sub safeglob($);
   sub getMimeTypeFromSuffix($);
   sub head($;$);
   sub scan_directives(@);
   sub doAnnotate($$);
   sub doCheckout($$);
   sub cvswebMarkup($$$);
   sub viewable($);
   sub doDiff($$$$$$);
   sub getDirLogs($$@);
   sub readLog($;$);
   sub printLog($;$);
   sub doLog($);
   sub flush_diff_rows($$$$);
   sub human_readable_diff($);
   sub navigateHeader($$$$$);
   sub plural_write($$);
   sub readableTime($$);
   sub clickablePath($$);
   sub chooseCVSRoot();
   sub chooseMirror();
   sub fileSortCmp();
   sub download_url($$;$);
   sub download_link($$$;$);
   sub toggleQuery($$);
   sub urlencode($);
   sub htmlquote($);
   sub htmlunquote($);
   sub http_header(;$);
   sub html_header($);
   sub html_footer();
   sub link_tags($);
   sub forbidden_module($);
   
 ##### Start of Configuration Area ########  ##### Start of Configuration Area ########
 # == EDIT this ==  use Cwd;
 # User configuration is stored in  
 $config = defined($ENV{CVSWEB_CONFIG}) ? $ENV{CVSWEB_CONFIG} : '/usr/local/etc/cvsweb.conf';  
   
   # == EDIT this ==
   # Locations to search for user configuration, in order:
   for (
        $ENV{CVSWEB_CONFIG},
        '/usr/local/etc/cvsweb.conf',
        getcwd() . '/cvsweb.conf'
       ) {
       $config = $_ if defined($_) && -r $_;
   }
   
 # == Configuration defaults ==  # == Configuration defaults ==
 # Defaults for configuration variables that shouldn't need  # Defaults for configuration variables that shouldn't need
 # to be configured..  # to be configured..
Line 95  $allow_version_select = 1;
Line 152  $allow_version_select = 1;
 # These are defined to allow checking with perl -cw  # These are defined to allow checking with perl -cw
 %CVSROOT = %MIRRORS = %DEFAULTVALUE = %ICONS = %MTYPES =  %CVSROOT = %MIRRORS = %DEFAULTVALUE = %ICONS = %MTYPES =
 %tags = %alltags = @tabcolors = ();  %tags = %alltags = @tabcolors = ();
 $cvstreedefault = $body_tag = $logo = $defaulttitle = $address =  $cvstreedefault = $body_tag = $body_tag_for_src =
 $backcolor = $long_intro = $short_instruction = $shortLogLen =  $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 104  $diffcolorChange = $diffcolorAdd = $diffcolorDarkChang
Line 162  $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 =
 $checkout_magic = $show_subdir_lastmod = $show_log_in_markup = $v =  $show_subdir_lastmod = $show_log_in_markup = $v =
 $navigationHeaderColor = $tableBorderColor = $markupLogColor =  $navigationHeaderColor = $tableBorderColor = $markupLogColor =
 $tabstop = $use_moddate = $moddate = undef;  $tabstop = $use_moddate = $moddate = $gzip_open = undef;
   
   $LOG_FILESEPARATOR = q/^={77}$/;
   $LOG_REVSEPARATOR = q/^-{28}$/;
   
   @DIFFTYPES = qw(h H u c s);
   @DIFFTYPES{@DIFFTYPES} = (
                             {
                              'descr'   => 'colored',
                              'opts'    => [ '-u' ],
                              'colored' => 1,
                             },
                             {
                              'descr'   => 'long colored',
                              'opts'    => [ '--unified=15' ],
                              'colored' => 1,
                             },
                             {
                              'descr'   => 'unified',
                              'opts'    => [ '-u' ],
                              'colored' => 0,
                             },
                             {
                              'descr'   => 'context',
                              'opts'    => [ '-c' ],
                              'colored' => 0,
                             },
                             {
                              'descr'   => 'side by side',
                              'opts'    => [ '--side-by-side', '--width=164' ],
                              'colored' => 0,
                             },
                            );
   
   @LOGSORTKEYS = qw(cvs date rev);
   @LOGSORTKEYS{@LOGSORTKEYS} = (
                                 {
                                  'descr' => 'Not sorted',
                                 },
                                 {
                                  'descr' => 'Commit date',
                                 },
                                 {
                                  'descr' => 'Revision',
                                 },
                                );
   
   
 ##### End of configuration variables #####  ##### 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} : '';
Line 133  $is_mod_perl = defined($ENV{MOD_PERL});
Line 244  $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_lynx || $is_w3m);  $is_textbased = ($is_links || $is_lynx || $is_w3m);
   
 $nofilelinks = $is_textbased;  $nofilelinks = $is_textbased;
   
Line 150  $nofilelinks = $is_textbased;
Line 262  $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. piping does  # Turn off gzip if running under mod_perl and no zlib is available,
 # not work as expected inside the server. One can probably  # piping does not work as expected inside the server.
 # achieve the same result using Apache::GZIPFilter.  $maycompress = (((defined($ENV{HTTP_ACCEPT_ENCODING})
 $maycompress =(($ENV{HTTP_ACCEPT_ENCODING} =~ m`gzip`                   && $ENV{HTTP_ACCEPT_ENCODING} =~ m`gzip`)
                 || $is_mozilla3)                   || $is_mozilla3)
                && !$is_msie                  && !$is_msie
                && !$is_mod_perl);                  && !($is_mod_perl && !$has_zlib));
   
 # 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
 # their current value) to any link/query string  # their current value) to any link/query string
 # you construct  # you construct
 @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) {
     do $config;     do $config
 }       || &fatal("500 Internal Error",
 else {                 sprintf('Error in loading configuration file: %s<BR><BR>%s<BR>',
                          $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 178  else {
Line 292  else {
 undef %input;  undef %input;
 $query = $ENV{QUERY_STRING};  $query = $ENV{QUERY_STRING};
   
 if ($query ne '') {  if (defined($query) && $query ne '') {
     foreach (split(/&/, $query)) {      foreach (split(/&/, $query)) {
         s/%(..)/sprintf("%c", hex($1))/ge;      # unquote %-quoted          s/%(..)/sprintf("%c", hex($1))/ge;      # unquote %-quoted
         if (/(\S+)=(.*)/) {          if (/(\S+)=(.*)/) {
Line 190  if ($query ne '') {
Line 304  if ($query ne '') {
     }      }
 }  }
   
 # For backwards compability, set only_with_tag to only_on_branch if set.  # For backwards compability, set only_with_tag to only_on_branch if set.
 $input{only_with_tag} = $input{only_on_branch}  $input{only_with_tag} = $input{only_on_branch}
     if (defined($input{only_on_branch}));      if (defined($input{only_on_branch}));
   
Line 217  foreach (keys %DEFAULTVALUE)
Line 331  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{$_})) {
         if ($barequery) {          push @barequery, join('=', urlencode($_), urlencode($input{$_}));
             $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 = "&amp;" . $barequery;      $barequery = "&$barequery";
 }  }
 else {  else {
     $query = "";      $query = "";
 }  }
   undef @barequery;
   
 # get actual parameters  # get actual parameters
 $sortby = $input{"sortby"};  $sortby = $input{"sortby"};
Line 262  else {
Line 375  else {
     $byfile = 1;      $byfile = 1;
 }  }
   
 $hr_default = $input{'f'} eq 'h';  $defaultDiffType = $input{'f'};
   
 $logsort = $input{'logsort'};  $logsort = $input{'logsort'};
   
Line 285  if ($input{'cvsroot'} && $CVSROOT{$input{'cvsroot'}}) 
Line 398  if ($input{'cvsroot'} && $CVSROOT{$input{'cvsroot'}}) 
 $cvsroot = $CVSROOT{$cvstree};  $cvsroot = $CVSROOT{$cvstree};
   
 # create icons out of description  # create icons out of description
 foreach my $k (keys %ICONS) {  my $k;
   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"} = "<IMG SRC=\"$ipath\" ALT=\"$itxt\" BORDER=\"0\" WIDTH=\"$iwidth\" HEIGHT=\"$iheight\">";          ${"${k}icon"} = sprintf('<IMG SRC="%s" ALT="%s" BORDER="0" WIDTH="%d" HEIGHT="%d">',
                                   htmlquote($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
 do "$config-$cvstree" if (-f "$config-$cvstree");  if (-f $config_cvstree) {
      do $config_cvstree
        || &fatal("500 Internal Error",
                  sprintf('Error in loading configuration file: %s<BR><BR>%s<BR>',
                          $config_cvstree, &htmlify($@)));
   }
   undef $config_cvstree;
   
 $prcategories = '(?:' . join('|', @prcategories) . ')';  $prcategories = '(?:' . join('|', @prcategories) . ')';
   $prcgi .= '%s' if defined($prcgi) && $prcgi !~ /%s/;
   
 $fullname = $cvsroot . '/' . $where;  $fullname = $cvsroot . '/' . $where;
 $mimetype = &getMimeTypeFromSuffix ($fullname);  $mimetype = &getMimeTypeFromSuffix ($fullname);
Line 309  $defaultViewable = $allow_markup && viewable($mimetype
Line 434  $defaultViewable = $allow_markup && viewable($mimetype
 # search for GZIP if compression allowed  # search for GZIP if compression allowed
 # We've to find out if the GZIP-binary exists .. otherwise  # We've to find out if the GZIP-binary exists .. otherwise
 # ge get an Internal Server Error if we try to pipe the  # ge get an Internal Server Error if we try to pipe the
 # output through the nonexistent gzip ..  # output through the nonexistent gzip ..
 # any more elegant ways to prevent this are welcome!  # any more elegant ways to prevent this are welcome!
 if ($allow_compress && $maycompress) {  if ($allow_compress && $maycompress && !$has_zlib) {
     foreach (split(/:/, $ENV{PATH})) {      foreach (split(/:/, $ENV{PATH})) {
         if (-x "$_/gzip") {          if (-x "$_/gzip") {
             $GZIPBIN = "$_/gzip";              $GZIPBIN = "$_/gzip";
Line 390  elsif (-d $fullname) {
Line 515  elsif (-d $fullname) {
                 $input{only_with_tag};                  $input{only_with_tag};
   
         }          }
   
   
   
         print "<HR NOSHADE>\n";          print "<HR NOSHADE>\n";
         # Using <MENU> in this manner violates the HTML2.0 spec but          # Using <MENU> in this manner violates the HTML2.0 spec but
         # provides the results that I want in most browsers.  Another          # provides the results that I want in most browsers.  Another
         # case of layout spooging up HTML.          # case of layout spooging up HTML.
   
         my $infocols = 0;          my $infocols = 0;
         if ($dirtable) {          if ($dirtable) {
             if (defined($tableBorderColor)) {              if (defined($tableBorderColor)) {
Line 405  elsif (-d $fullname) {
Line 530  elsif (-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++;
             print "<tr><th align=left bgcolor=\"" . (($byfile) ?              printf '<tr><th align=left bgcolor="%s">',
                                                    $columnHeaderColorSorted :                $byfile ? $columnHeaderColorSorted : $columnHeaderColorDefault;
                                                    $columnHeaderColorDefault) . "\">";              if ($byfile) {
             print "<a href=\"./" . &toggleQuery("sortby","file") .                  print 'File';
                 "#dirlist\">" if (!$byfile);              } else {
             print "File";                  print &link('File', sprintf('./%s#dirlist',
             print "</a>" if (!$byfile);                                              &toggleQuery("sortby", "file")));
               }
             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++;
                 print "<th align=left bgcolor=\"" . (($byrev) ?                  printf '<th align=left bgcolor="%s">',
                                                    $columnHeaderColorSorted :                    $byrev ? $columnHeaderColorSorted : $columnHeaderColorDefault;
                                                    $columnHeaderColorDefault) . "\">";                  if ($byrev) {
                 print "<a href=\"./" . &toggleQuery ("sortby","rev") .                      print 'Rev.';
                     "#dirlist\">" if (!$byrev);                  } else {
                 print "Rev.";                      print &link('Rev.', sprintf('./%s#dirlist',
                 print "</a>" if (!$byrev);                                                  &toggleQuery("sortby", "rev")));
                   }
                 print "</th>";                  print "</th>";
                 $infocols++;                  $infocols++;
                 print "<th align=left bgcolor=\"" . (($bydate) ?                  printf '<th align=left bgcolor="%s">',
                                                    $columnHeaderColorSorted :                    $bydate ? $columnHeaderColorSorted : $columnHeaderColorDefault;
                                                    $columnHeaderColorDefault) . "\">";                  if ($bydate) {
                 print "<a href=\"./" . &toggleQuery ("sortby","date") .                      print 'Age';
                     "#dirlist\">" if (!$bydate);                  } else {
                 print "Age";                      print &link('Age', sprintf('./%s#dirlist',
                 print "</a>" if (!$bydate);                                                 &toggleQuery("sortby", "date")));
                   }
                 print "</th>";                  print "</th>";
                 if ($show_author) {                  if ($show_author) {
                     $infocols++;                      $infocols++;
                     print "<th align=left bgcolor=\"" . (($byauthor) ?                      printf '<th align=left bgcolor="%s">',
                                                    $columnHeaderColorSorted :                        $byauthor ? $columnHeaderColorSorted : $columnHeaderColorDefault;
                                                    $columnHeaderColorDefault) . "\">";                      if ($byauthor) {
                     print "<a href=\"./" . &toggleQuery ("sortby","author") .                          print 'Author';
                             "#dirlist\">" if (!$byauthor);                      } else {
                     print "Author";                          print &link('Author', sprintf('./%s#dirlist',
                     print "</a>" if (!$byauthor);                                                        &toggleQuery("sortby", "author")));
                       }
                     print "</th>";                      print "</th>";
                 }                  }
                 $infocols++;                  $infocols++;
                 print "<th align=left bgcolor=\"" . (($bylog) ?                  printf '<th align=left bgcolor="%s">',
                                                $columnHeaderColorSorted :                    $bylog ? $columnHeaderColorSorted : $columnHeaderColorDefault;
                                                $columnHeaderColorDefault) . "\">";                  if ($bylog) {
                 print "<a href=\"./", toggleQuery("sortby","log"), "#dirlist\">" if (!$bylog);                      print 'Last log entry';
                 print "Last log entry";                  } else {
                 print "</a>" if (!$bylog);                      print &link('Last log entry', sprintf('./%s#dirlist',
                                                             &toggleQuery("sortby", "log")));
                   }
                 print "</th>";                  print "</th>";
             }              }
             elsif ($use_descriptions) {              elsif ($use_descriptions) {
                 print "<th align=left bgcolor=\"". $columnHeaderColorDefault . "\">";                  printf '<th align=left bgcolor="%s">', $columnHeaderColorDefault;
                 print "Description";                  print "Description";
                 $infocols++;                  $infocols++;
             }              }
Line 465  elsif (-d $fullname) {
Line 596  elsif (-d $fullname) {
             print "<menu>\n";              print "<menu>\n";
         }          }
         my $dirrow = 0;          my $dirrow = 0;
   
         my $i;          my $i;
         lookingforattic:          lookingforattic:
         for ($i = 0; $i <= $#dir; $i++) {          for ($i = 0; $i <= $#dir; $i++) {
Line 480  elsif (-d $fullname) {
Line 611  elsif (-d $fullname) {
             closedir($dh);              closedir($dh);
         }          }
   
         my $hideAtticToggleLink = "<a href=\"./" .          my $hideAtticToggleLink = $input{'hideattic'} ? '' :
                 &toggleQuery ("hideattic") .            &link('[Hide]', sprintf('./%s#dirlist',
                 "#dirlist\">[Hide]</a>" if (!$input{'hideattic'});                                    &toggleQuery ("hideattic")));
   
         # Sort without the Attic/ pathname.          # Sort without the Attic/ pathname.
         # place directories first          # place directories first
Line 520  elsif (-d $fullname) {
Line 651  elsif (-d $fullname) {
                 next if ($_ eq '..' && $where eq '/');                  next if ($_ eq '..' && $where eq '/');
                 my ($rev,$date,$log,$author,$filename) = @{$fileinfo{$_}}                  my ($rev,$date,$log,$author,$filename) = @{$fileinfo{$_}}
                     if (defined($fileinfo{$_}));                      if (defined($fileinfo{$_}));
                 print "<tr bgcolor=\"" . @tabcolors[$dirrow%2] . "\"><td>" if ($dirtable);                  printf '<tr bgcolor="%s"><td>', $tabcolors[$dirrow % 2] 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; <a href=\"./" .                          print "&nbsp; ";
                             &toggleQuery ("hideattic") .                          print &link("[Don't hide]", sprintf('./%s#dirlist',
                                 "#dirlist\">[Don't hide]</a>";                                                              &toggleQuery ("hideattic")));
                     }                      }
                 }                  }
                 # 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 562  elsif (-d $fullname) {
Line 693  elsif (-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 573  elsif (-d $fullname) {
Line 704  elsif (-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 604  elsif (-d $fullname) {
Line 735  elsif (-d $fullname) {
                 next if (!defined($fileinfo{$_}));                  next if (!defined($fileinfo{$_}));
                 ($rev,$date,$log,$author) = @{$fileinfo{$_}};                  ($rev,$date,$log,$author) = @{$fileinfo{$_}};
                 $filesfound++;                  $filesfound++;
                 print "<tr bgcolor=\"" . @tabcolors[$dirrow%2] . "\"><td>" if ($dirtable);                  printf '<tr bgcolor="%s"><td>', $tabcolors[$dirrow % 2] if $dirtable;
                 print "<A NAME=\"$_\"></A>";                  print "<A NAME=\"$_\"></A>";
                 if ($nofilelinks) {                  if ($nofilelinks) {
                     print $fileicon;                      print $fileicon;
Line 615  elsif (-d $fullname) {
Line 746  elsif (-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 627  elsif (-d $fullname) {
Line 758  elsif (-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 642  elsif (-d $fullname) {
Line 773  elsif (-d $fullname) {
         if ($dirtable && defined($tableBorderColor)) {          if ($dirtable && defined($tableBorderColor)) {
             print "</td></tr></table>";              print "</td></tr></table>";
         }          }
         print "". ($dirtable == 1) ? "</table>" : "</menu>" . "\n";          print( $dirtable == 1 ? "</table>\n" : "</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";
         }          }
         if ($input{only_with_tag} && (!%tags || !$tags{$input{only_with_tag}})) {          if ($input{only_with_tag} && (!%tags || !$tags{$input{only_with_tag}})) {
             %tags = %alltags              %tags = %alltags
         }          }
         if (scalar %tags          if (scalar %tags
             || $input{only_with_tag}              || $input{only_with_tag}
             || $edit_option_form              || $edit_option_form
             || defined($input{"options"})) {              || defined($input{"options"})) {
             print "<hr size=1 NOSHADE>";              print "<hr size=1 NOSHADE>";
Line 662  elsif (-d $fullname) {
Line 793  elsif (-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})
                         && $input{$var} ne $DEFAULTVALUE{$var}                          && (!defined($DEFAULTVALUE{$var})
                               || $input{$var} ne $DEFAULTVALUE{$var})
                         && $input{$var} ne ""                          && $input{$var} ne ""
                         && $var ne "only_with_tag");                          && $var ne "only_with_tag");
             }              }
Line 672  elsif (-d $fullname) {
Line 804  elsif (-d $fullname) {
             print ">";              print ">";
             print "<OPTION VALUE=\"\">All tags / default branch\n";              print "<OPTION VALUE=\"\">All tags / default branch\n";
             foreach my $tag (reverse sort { lc $a cmp lc $b } keys %tags) {              foreach my $tag (reverse sort { lc $a cmp lc $b } keys %tags) {
                 print "<OPTION",defined($input{only_with_tag}) &&                  print "<OPTION",defined($input{only_with_tag}) &&
                        $input{only_with_tag} eq $tag ? " SELECTED":"",                         $input{only_with_tag} eq $tag ? " SELECTED" : "",
                        ">$tag\n";                         ">$tag\n";
             }              }
             print "</SELECT>\n";              print "</SELECT>\n";
Line 699  elsif (-d $fullname) {
Line 831  elsif (-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>revisions by: \n";              print "<td>Sort log by: ";
             print "<SELECT NAME=logsort>\n";              printLogSortSelect(0);
             print "<OPTION VALUE=cvs",$logsort eq "cvs" ? " SELECTED" : "", ">Not sorted";              print "</td></tr>";
             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();              printDiffSelect(0);
             print "</td>";              print "</td>";
             print "<td>Show Attic files: ";              print "<td>Show Attic files: ";
             print "<INPUT NAME=hideattic TYPE=CHECKBOX", $input{'hideattic'}?" CHECKED":"",              print "<INPUT NAME=hideattic TYPE=CHECKBOX", $input{'hideattic'} ? " CHECKED" : "",
             "></td></tr>\n";              "></td></tr>\n";
             print "<tr><td align=center colspan=2><input type=submit value=\"Change Options\">";              print "<tr><td align=center colspan=2><input type=submit value=\"Change Options\">";
             print "</td></tr></table></center></FORM>\n";              print "</td></tr></table></center></FORM>\n";
         }          }
         print &html_footer;          print &html_footer;
         print "</BODY></HTML>\n";          print "</BODY></HTML>\n";
     }      }
   
 ###############################  ###############################
 # View Files  # View Files
Line 724  elsif (-d $fullname) {
Line 853  elsif (-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 755  elsif (-d $fullname) {
Line 887  elsif (-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 788  elsif (-d $fullname) {
Line 921  elsif (-d $fullname) {
         }          }
         &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) = @_;
     $use_java_script = 0 if (!defined($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 findLastModifiedSubdirs {  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(@) {
     my (@dirs) = @_;      my (@dirs) = @_;
     my ($dirname, @files);      my ($dirname, @files);
   
Line 839  sub findLastModifiedSubdirs {
Line 998  sub findLastModifiedSubdirs {
     return @files;      return @files;
 }  }
   
 sub htmlify {  sub htmlify_sub(&$) {
         my($string, $pr) = @_;      (my $proc, local $_) = @_;
       local @_ = split(m`(<a [^>]+>[^<]*</a>)`i);
       my ($linked, $result);
   
         # Special Characters; RFC 1866      while (($_, $linked) = splice(@_, 0, 2)) {
         $string =~ s/&/&amp;/g;          &$proc();
         $string =~ s/\"/&quot;/g;          $result .= $_;
         $string =~ s/</&lt;/g;          $result .= $linked;
         $string =~ s/>/&gt;/g;      }
   
         # get URL's as link ..      $result;
         $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>`;  }
         # 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>`;  
   
         # get #PR as link ..  sub htmlify($;$) {
         if ($pr && defined($prcgi)) {      (local $_, my $extra) = @_;
             1 while $string =~ s`\b(pr[:#]?\s*(?:#?\d+[,\s]\s*)*#?)(\d+)\b`$1<A HREF="$prcgi$2">$2</A>`i;  
             $string =~ s`\b${prcategories}/(\d+)\b`<A HREF="$prcgi$1">$&</A>`igo;      $_ = htmlquote($_);
   
       # get URL's as link
       s{
         (http|ftp|https)://\S+
        }{
            &link($&, htmlunquote($&))
        }egx;
   
       # get e-mails as link
       $_ = htmlify_sub {
           s<
             [\w+=\-.!]+@[\w\-]+(\.[\w\-]+)+
               ><
                 &link($&, "mailto:$&")
                   >egix;
       } $_;
   
       if ($extra) {
           # get PR #'s as link: "PR#nnnn" "PR: nnnn, ..." "PR nnnn, ..." "bin/nnnn"
           if (defined($prcgi)) {
               my $prev;
   
               do {
                   $prev = $_;
   
                   $_ = htmlify_sub {
                       s{
                         (\bPR[:\#]?\s*
                          (?:
                           \#?
                           \d+[,\s]\s*
                          )*
                          \#?)
                         (\d+)\b
                        }{
                            $1 . &link($2, sprintf($prcgi, $2)) . $3
                        }egix;
                   } $_;
               } while ($_ ne $prev);
   
               $_ = htmlify_sub {
                   s{
                     (\b$prcategories/(\d+)\b)
                    }{
                        &link($1, sprintf($prcgi, $2)) . $3
                    }egox;
               } $_;
         }          }
   
         return $string;          # get manpage specs as link: "foo.1" "foo(1)"
           if (defined($mancgi)) {
               $_ = htmlify_sub {
                   s{
                     (\b([a-zA-Z][\w_.]+)
                      (?:
                       \( ([0-9n]) \)\B
                       |
                       \.([0-9n])\b
                      )
                     )
                    }{
                        &link($1, sprintf($mancgi, $3 ne '' ? $3 : $4, $2)) . $5
                    }egx;
               } $_;
           }
       }
   
       $_;
 }  }
   
 sub spacedHtmlText {  sub spacedHtmlText($;$) {
         my($string, $pr) = @_;          local $_ = $_[0];
           my $ts = $_[1] || $tabstop;
   
         # Cut trailing spaces          # Cut trailing spaces and tabs
         s/\s+$//;          s/[ \t]+$//;
   
         # Expand tabs          if (defined($ts)) {
         $string =~ s/\t+/' ' x (length($&) * $tabstop - length($`) % $tabstop)/e              # Expand tabs
             if (defined($tabstop));              1 while s/\t+/' ' x (length($&) * $ts - length($`) % $ts)/e
           }
   
         # 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 :-)
         $string =~ s/\001/\001%/g; #protect our & substitute  
         if ($hr_breakable) {          if ($hr_breakable) {
             # make every other space 'breakable'              # make every other space 'breakable'
             $string =~ s/       / \001nbsp; \001nbsp; \001nbsp; \001nbsp;/g;    # <tab>              s/  / \001nbsp;/g;                              # 2 * <space>
             $string =~ s/  / \001nbsp;/g;                              # 2 * <space>  
             # leave single space as it is              # leave single space as it is
           } else {
               s/ /\001nbsp;/g;
         }          }
         else {  
             $string =~ s/       /\001nbsp;\001nbsp;\001nbsp;\001nbsp;\001nbsp;\001nbsp;\001nbsp;\001nbsp;/g;  
             $string =~ s/ /\001nbsp;/g;  
         }  
   
         $string = htmlify($string);          $_ = htmlify($_);
   
         # unescape          # unescape
         $string =~ s/\001([^%])/&$1/g;          y/\001/&/;
         $string =~ s/\001%/\001/g;  
   
         return $string;          return $_;
 }  }
   
 sub link {  sub link($$) {
         my($name, $where) = @_;          my($name, $where) = @_;
   
         return "<A HREF=\"$where\">$name</A>\n";          sprintf '<A HREF="%s">%s</A>', htmlquote($where), $name;
 }  }
   
 sub revcmp {  sub revcmp($$) {
         my($rev1, $rev2) = @_;          my($rev1, $rev2) = @_;
   
           # make no comparison for a tag or a branch
           return 0 if $rev1 =~ /[^\d.]/ || $rev2 =~ /[^\d.]/;
   
         my(@r1) = split(/\./, $rev1);          my(@r1) = split(/\./, $rev1);
         my(@r2) = split(/\./, $rev2);          my(@r2) = split(/\./, $rev2);
         my($a,$b);          my($a,$b);
Line 917  sub revcmp {
Line 1142  sub revcmp {
         return 0;          return 0;
 }  }
   
 sub fatal {  sub fatal($$) {
         my($errcode, $errmsg) = @_;          my($errcode, $errmsg) = @_;
         if ($is_mod_perl) {          if ($is_mod_perl) {
                 Apache->request->status((split(/ /, $errcode))[0]);                  Apache->request->status((split(/ /, $errcode))[0]);
Line 931  sub fatal {
Line 1156  sub fatal {
         exit(1);          exit(1);
 }  }
   
 sub redirect {  sub redirect($) {
         my($url) = @_;          my($url) = @_;
         if ($is_mod_perl) {          if ($is_mod_perl) {
                 Apache->request->status(301);                  Apache->request->status(301);
Line 942  sub redirect {
Line 1167  sub redirect {
                 print "Location: $url\r\n";                  print "Location: $url\r\n";
         }          }
         html_header("Moved");          html_header("Moved");
         print "This document is located <A HREF=$url>here</A>.\n";          print "This document is located ", &link('here', $url), "\n";
         print &html_footer;          print &html_footer;
         exit(1);          exit(1);
 }  }
   
 sub safeglob {  sub safeglob($) {
         my ($filename) = @_;          my ($filename) = @_;
         my ($dirname);          my ($dirname);
         my (@results);          my (@results);
Line 968  sub safeglob {
Line 1193  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 976  sub safeglob {
Line 1201  sub safeglob {
         @results;          @results;
 }  }
   
 sub getMimeTypeFromSuffix {  sub getMimeTypeFromSuffix($) {
     my ($fullname) = @_;      my ($fullname) = @_;
     my ($mimetype, $suffix);      my ($mimetype, $suffix);
     my $fh = do {local(*FH);};      my $fh = do {local(*FH);};
Line 984  sub getMimeTypeFromSuffix {
Line 1209  sub getMimeTypeFromSuffix {
     ($suffix = $fullname) =~ s/^.*\.([^.]*)$/$1/;      ($suffix = $fullname) =~ s/^.*\.([^.]*)$/$1/;
     $mimetype = $MTYPES{$suffix};      $mimetype = $MTYPES{$suffix};
     $mimetype = $MTYPES{'*'} if (!$mimetype);      $mimetype = $MTYPES{'*'} if (!$mimetype);
   
     if (!$mimetype && -f $mime_types) {      if (!$mimetype && -f $mime_types) {
         # okey, this is something special - search the          # okey, this is something special - search the
         # mime.types database          # mime.types database
Line 997  sub getMimeTypeFromSuffix {
Line 1222  sub getMimeTypeFromSuffix {
         }          }
         close ($fh);          close ($fh);
     }      }
   
 # okey, didn't find anything useful ..  # okey, didn't find anything useful ..
     if (!($mimetype =~ /\S\/\S/)) {      if (!($mimetype =~ /\S\/\S/)) {
         $mimetype = "text/plain";          $mimetype = "text/plain";
Line 1006  sub getMimeTypeFromSuffix {
Line 1231  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($$) {
     my ($rev) = @_;      my ($rev) = @_;
     my ($pid);      my ($pid);
     my ($pathname, $filename);      my ($pathname, $filename);
Line 1017  sub doAnnotate ($$) {
Line 1276  sub doAnnotate ($$) {
   
     # make sure the revisions a wellformed, for security      # make sure the revisions a wellformed, for security
     # reasons ..      # reasons ..
     if (!($rev =~ /^[\d\.]+$/)) {      if ($rev =~ /[^\w.]/) {
         &fatal("404 Not Found",          &fatal("404 Not Found",
                 "Malformed query \"$ENV{QUERY_STRING}\"");                  "Malformed query \"$ENV{QUERY_STRING}\"");
     }      }
Line 1025  sub doAnnotate ($$) {
Line 1284  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 1039  sub doAnnotate ($$) {
Line 1293  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 server") || fatal ("500 Internal Error",      $pid = open2($reader, $writer, "cvs -Rl server") || 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
     #     Argument foo/xx      #     Argument foo/xx
Line 1051  sub doAnnotate ($$) {
Line 1305  sub doAnnotate ($$) {
     #     /home/kingdon/zwork/cvsroot      #     /home/kingdon/zwork/cvsroot
     #     annotate      #     annotate
     # although as you can see there are a few more details.      # although as you can see there are a few more details.
   
     print $writer "Root $cvsroot\n";      print $writer "Root $cvsroot\n";
     print $writer "Valid-responses ok error Valid-requests Checked-in Updated Merged Removed M E\n";      print $writer "Valid-responses ok error Valid-requests Checked-in Updated Merged Removed M E\n";
     # Don't worry about sending valid-requests, the server just needs to      # Don't worry about sending valid-requests, the server just needs to
Line 1071  sub doAnnotate ($$) {
Line 1325  sub doAnnotate ($$) {
         if ($path eq "") {          if ($path eq "") {
             # In our example, $_ is "dir".              # In our example, $_ is "dir".
             $path = $_;              $path = $_;
         }          }
         else {          else {
             print $writer "Directory $path\n";              print $writer "Directory $path\n";
             print $writer "$cvsroot/$path\n";              print $writer "$cvsroot/$path\n";
Line 1084  sub doAnnotate ($$) {
Line 1338  sub doAnnotate ($$) {
     # And the last "Directory" before "annotate" is the top level.      # And the last "Directory" before "annotate" is the top level.
     print $writer "Directory .\n";      print $writer "Directory .\n";
     print $writer "$cvsroot\n";      print $writer "$cvsroot\n";
   
     print $writer "annotate\n";      print $writer "annotate\n";
     # OK, we've sent our command to the server.  Thing to do is to      # OK, we've sent our command to the server.  Thing to do is to
     # close the writer side and get all the responses.  If "cvs server"      # close the writer side and get all the responses.  If "cvs server"
     # 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
     #     E ***************      #     E ***************
     #     M 1.3          (kingdon  06-Sep-97): hello      #     M 1.3          (kingdon  06-Sep-97): hello
     #     ok      #     ok
     my ($lineNr) = 0;      my ($lineNr) = 0;
     my ($oldLrev, $oldLusr) = ("", "");      my ($oldLrev, $oldLusr) = ("", "");
Line 1106  sub doAnnotate ($$) {
Line 1365  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 1115  sub doAnnotate ($$) {
Line 1382  sub doAnnotate ($$) {
         }          }
         elsif ($words[0] eq "M") {          elsif ($words[0] eq "M") {
             $lineNr++;              $lineNr++;
             my $lrev = substr ($_, 2, 13);              (my $lrev = substr($_, 2, 13)) =~ y/ //d;
             my $lusr = substr ($_, 16,  9);              (my $lusr = substr($_, 16,  9)) =~ y/ //d;
             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 = "             ";                  $revprint = sprintf('%-8s', '');
             }              }
             else {              else {
                 $revprint = $lrev; $oldLusr = "";                  $revprint = sprintf('%-8s', $lrev);
                   $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;              # Set bold for text-based browsers only - graphical
             my $isCurrentRev = ($rev eq $lrev);              # browsers show bold fonts a bit wider than regular fonts,
               # so it looks irregular.
             print "<b>" if ($isCurrentRev);              print "<b>" if ($isCurrentRev && $is_textbased);
             printf ("%8s%s%8s %4d:", $revprint, ($isCurrentRev ? "|" : " "), $usrprint, $lineNr);  
             print spacedHtmlText($line);              printf "%s%s %-8s %4d:",
             print "</b>" if ($isCurrentRev);                      $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 1163  sub doAnnotate ($$) {
Line 1439  sub doAnnotate ($$) {
 ###############################  ###############################
 # make Checkout  # make Checkout
 ###############################  ###############################
 sub doCheckout {  sub doCheckout($$) {
     my ($fullname, $rev) = @_;      my ($fullname, $rev) = @_;
     my ($mimetype,$revopt);      my ($mimetype,$revopt);
     my $fh = do {local(*FH);};      my $fh = do {local(*FH);};
   
       if ($rev eq 'HEAD' || $rev eq '.') {
           $rev = undef;
       }
   
     # make sure the revisions a wellformed, for security      # make sure the revisions a wellformed, for security
     # reasons ..      # reasons ..
     if (defined($rev) && !($rev =~ /^[\d\.]+$/)) {      if (defined($rev) && $rev =~ /[^\w.]/) {
         &fatal("404 Not Found",          &fatal("404 Not Found",
                 "Malformed query \"$ENV{QUERY_STRING}\"");                  "Malformed query \"$ENV{QUERY_STRING}\"");
     }      }
Line 1197  sub doCheckout {
Line 1477  sub doCheckout {
             $moddate=$date{$symrev{HEAD}};              $moddate=$date{$symrev{HEAD}};
         }          }
     }      }
   
     ### just for the record:      ### just for the record:
     ### 'cvs co' seems to have a bug regarding single checkout of      ### 'cvs co' seems to have a bug regarding single checkout of
     ### directories/files having spaces in it;      ### directories/files having spaces in it;
Line 1206  sub doCheckout {
Line 1486  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("cvs", "-d", $cvsroot, "co", "-p", $revopt, $where);        exec("cvs", "-Rld", $cvsroot, "co", "-p", $revopt, $where);
     }      }
 #===================================================================  #===================================================================
 #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 1246  sub doCheckout {
Line 1526  sub doCheckout {
     close($fh);      close($fh);
 }  }
   
 sub cvswebMarkup {  sub cvswebMarkup($$$) {
     my ($filehandle,$fullname,$revision) = @_;      my ($filehandle,$fullname,$revision) = @_;
     my ($pathname, $filename);      my ($pathname, $filename);
   
Line 1256  sub cvswebMarkup {
Line 1536  sub cvswebMarkup {
   
     http_header();      http_header();
   
     navigateHeader ($scriptwhere, $pathname, $filename, $revision, "view");      navigateHeader($scriptwhere, $pathname, $filename, $revision, "view");
     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(urlencode($fileurl), $revision, "(download)");      &download_link($fileurl, $revision, "download");
       print ")";
     if (!$defaultTextPlain) {      if (!$defaultTextPlain) {
         print "&nbsp;";          print "&nbsp;(";
         &download_link(urlencode($fileurl), $revision, "(as text)",          &download_link($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 1278  sub cvswebMarkup {
Line 1560  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/) {
         print "<IMG SRC=\"$url$barequery\"><BR>";          printf '<IMG SRC="%s"><BR>', htmlquote("$url$barequery");
     }      }
       elsif ($mimetype =~ m%^application/pdf%) {
           printf '<EMBED SRC="%s" WIDTH="100%"><BR>', htmlquote("$url$barequery");
       }
     else {      else {
         print "<PRE>";          print "<PRE>";
         foreach (@content) {  
             print spacedHtmlText($_);          # prefetch several lines
           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>";
     }      }
Line 1296  sub cvswebMarkup {
Line 1588  sub cvswebMarkup {
 sub viewable($) {  sub viewable($) {
     my ($mimetype) = @_;      my ($mimetype) = @_;
   
     $mimetype =~ m%^text/% ||      $mimetype =~ m%^((text|image)/|application/pdf)% ;
     $mimetype =~ m%^image/% ||  
     0;  
 }  }
   
 ###############################  ###############################
 # Show Colored Diff  # Show Colored Diff
 ###############################  ###############################
 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, @difftype, $diffname, $f1, $f2);          my ($rev1, $rev2, $sym1, $sym2, $f1, $f2);
   
         if ($r1 =~ /([^:]+)(:(.+))?/) {          if ($r1 =~ /([^:]+)(:(.+))?/) {
             $rev1 = $1;              $rev1 = $1;
Line 1325  sub doDiff {
Line 1615  sub doDiff {
             $rev2 = $tr2;              $rev2 = $tr2;
             $sym2 = "";              $sym2 = "";
         }          }
   
         # make sure the revisions a wellformed, for security          # make sure the revisions a wellformed, for security
         # reasons ..          # reasons ..
         if (!($rev1 =~ /^[\d\.]+$/) || !($rev2 =~ /^[\d\.]+$/)) {          if ($rev1 =~ /[^\w.]/ || $rev2 =~ /[^\w.]/) {
             &fatal("404 Not Found",              &fatal("404 Not Found",
                     "Malformed query \"$ENV{QUERY_STRING}\"");                      "Malformed query \"$ENV{QUERY_STRING}\"");
         }          }
Line 1340  sub doDiff {
Line 1631  sub doDiff {
             ($rev1, $sym1) = ($rev2, $sym2);              ($rev1, $sym1) = ($rev2, $sym2);
             ($rev2, $sym2) = ($tmp1, $tmp2);              ($rev2, $sym2) = ($tmp1, $tmp2);
         }          }
         my $human_readable = 0;          my $difftype = $DIFFTYPES{$f};
         if ($f eq 'c') {  
             @difftype = qw{-c};          if (!$difftype) {
             $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';              push @difftype, '-p' if $f ne 's';
   
             my($re1, $re2);              my($re1, $re2);
   
Line 1395  sub doDiff {
Line 1668  sub doDiff {
         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 1430  sub doDiff {
Line 1704  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 1448  sub doDiff {
Line 1722  sub doDiff {
 ###############################  ###############################
 # Show Logs ..  # Show Logs ..
 ###############################  ###############################
 sub getDirLogs {  sub getDirLogs($$@) {
     my ($cvsroot,$dirname,@otherFiles) = @_;      my ($cvsroot,$dirname,@otherFiles) = @_;
     my ($state,$otherFiles,$tag, $file, $date, $branchpoint, $branch, $log);      my ($state,$otherFiles,$tag, $file, $date, $branchpoint, $branch, $log);
     my ($rev, $revision, $revwanted, $filename, $head, $author);      my ($rev, $revision, $revwanted, $filename, $head, $author);
Line 1466  sub getDirLogs {
Line 1740  sub getDirLogs {
     }      }
   
     # just execute rlog if there are any files      # just execute rlog if there are any files
     if ($#files < 0) {      if ($#files < 0) {
         return;          return;
     }      }
   
Line 1511  again:
Line 1785  again:
         }          }
         if ($state eq "head" && /^symbolic names/) {          if ($state eq "head" && /^symbolic names/) {
             $state = "tags";              $state = "tags";
             ($branch = $head) =~ s/\.\d+$// if (!defined($branch));              ($branch = $head) =~ s/\.\d+$// if (!defined($branch));
             $branch =~ s/(\.?)(\d+)$/${1}0.$2/;              $branch =~ s/(\.?)(\d+)$/${1}0.$2/;
             $symrev{MAIN} = $branch;              $symrev{MAIN} = $branch;
             $symrev{HEAD} = $branch;              $symrev{HEAD} = $branch;
Line 1545  again:
Line 1819  again:
             $state = "head";              $state = "head";
             goto again;              goto again;
         }          }
         if ($state eq "head" && /^----------------------------$/) {          if ($state eq "head" && /$LOG_REVSEPARATOR/o) {
             $state = "log";              $state = "log";
             $rev = undef;              $rev = undef;
             $date = undef;              $date = undef;
Line 1555  again:
Line 1829  again:
             next;              next;
         }          }
         if ($state eq "log") {          if ($state eq "log") {
             if (/^----------------------------$/              if (/$LOG_REVSEPARATOR/o || /$LOG_FILESEPARATOR/o) {
                 || /^=============================/) {  
                 # End of a log entry.                  # End of a log entry.
                 my $revbranch;                  my $revbranch;
                 ($revbranch = $rev) =~ 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 (!defined($revwanted) && defined($branch)                  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"
Line 1600  again:
Line 1873  again:
                 next;                  next;
             }              }
             else {              else {
                 $log = $log . $_;                  $log .= $_;
             }              }
         }          }
         if (/^===============/) {          if (/$LOG_FILESEPARATOR/o) {
             $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>\$ENV{PATH}</b> in your configuration file correctly ?");                "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);
 }  }
   
 sub readLog {  sub readLog($;$) {
         my($fullname,$revision) = @_;          my($fullname,$revision) = @_;
         my ($symnames, $head, $rev, $br, $brp, $branch, $branchrev);          my ($symnames, $head, $rev, $br, $brp, $branch, $branchrev);
         my $fh = do {local(*FH);};          my $fh = do {local(*FH);};
Line 1677  sub readLog {
Line 1950  sub readLog {
 # log info  # log info
 # ----------------------------  # ----------------------------
         logentry:          logentry:
         while (!/^=========/) {          while (!/$LOG_FILESEPARATOR/o) {
             $_ = <$fh>;              $_ = <$fh>;
             last logentry if (!defined($_));    # EOF              last logentry if (!defined($_));    # EOF
             print "R:", $_ if ($verbose);              print "R:", $_ if ($verbose);
Line 1685  sub readLog {
Line 1958  sub readLog {
                 $rev = $1;                  $rev = $1;
                 unshift(@allrevisions,$rev);                  unshift(@allrevisions,$rev);
             }              }
             elsif (/^========/ || /^----------------------------$/) {              elsif (/$LOG_FILESEPARATOR/o || /$LOG_REVSEPARATOR/o) {
                 next logentry;                  next logentry;
             }              }
             else {              else {
Line 1719  sub readLog {
Line 1992  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 (/^----------------------------$/ || /^=========/);                  last line if (/$LOG_FILESEPARATOR/o || /$LOG_REVSEPARATOR/o);
                 $log{$rev} .= $_;                  $log{$rev} .= $_;
             }              }
             print "E:", $_ if ($verbose);              print "E:", $_ if ($verbose);
Line 1843  sub readLog {
Line 2116  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 1871  sub printLog($;$) {
Line 2158  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)",                  &download_link($fileurl, $_, "as text", "text/plain");
                            "text/plain");                  print ")";
             }              }
             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 " - <a href=\"" . $scriptname . "/" . urlencode($where) . "?annotate=$_$barequery\">";                  print " - ";
                 print "annotate</a>";                  print &link('annotate',
                               sprintf('%s/%s?annotate=%s%s',
                                       $scriptname,
                                       urlencode($where),
                                       $_,
                                       $barequery));
             }              }
             # Plus a select link if enabled, and this version isn't selected              # 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 " - <A HREF=\"${scriptwhere}?r1=$_$barequery" .                      print " - ";
                         "\">[select for diffs]</A>\n";                      print &link('[select for diffs]',
                                   sprintf('%s?r1=%s%s',
                                           $scriptwhere,
                                           $_,
                                           $barequery));
                 }                  }
                 else {                  else {
                     print " - <b>[selected]</b>";                      print " - <b>[selected]</b>";
Line 1908  sub printLog($;$) {
Line 2206  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 1948  sub printLog($;$) {
Line 2246  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";  
                 print "&amp;r2=$_" . $barequery . "\">$prev</A>\n";                  my $url = sprintf('%s.diff?r1=%s&r2=%s%s',
                 if (!$hr_default) { # offer a human readable version if not default                                    $scriptwhere,
                     print "(<A HREF=\"${scriptwhere}.diff?r1=$prev";                                    $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})) {
                 print " to branchpoint <A HREF=\"${scriptwhere}.diff?r1=$brp";                  my $url = sprintf('%s.diff?r1=%s&r2=%s%s',
                 print "&amp;r2=$_" . $barequery . "\">$brp</A>\n";                                    $scriptwhere,
                 if (!$hr_default) { # offer a human readable version if not default                                    $brp,
                 print "(<A HREF=\"${scriptwhere}.diff?r1=$brp";                                    $_,
                 print "&amp;r2=$_" . $barequery . "&amp;f=h\">colored</A>)\n";                                    $barequery);
                 }  
                   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 1989  sub printLog($;$) {
Line 2292  sub printLog($;$) {
                 }                  }
                 if (!defined($diffrev{$nextmain})) {                  if (!defined($diffrev{$nextmain})) {
                     $diffrev{$nextmain} = 1;                      $diffrev{$nextmain} = 1;
                     print " next main <A HREF=\"${scriptwhere}.diff?r1=$nextmain";  
                     print "&amp;r2=$_" . $barequery .                      my $url = sprintf('%s.diff?r1=%s&r2=%s%s',
                         "\">$nextmain</A>\n";                                        $scriptwhere,
                     if (!$hr_default) { # offer a human readable version if not default                                        $nextmain,
                         print "(<A HREF=\"${scriptwhere}.diff?r1=$nextmain";                                        $_,
                         print "&amp;r2=$_" . $barequery .                                        $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 2019  sub printLog($;$) {
Line 2323  sub printLog($;$) {
         print "</PRE>\n";          print "</PRE>\n";
 }  }
   
 sub doLog {  sub doLog($) {
         my($fullname) = @_;          my($fullname) = @_;
         my ($diffrev, $upwhere, $filename, $backurl);          my ($diffrev, $upwhere, $filename, $backurl);
   
         readLog($fullname);          readLog($fullname);
   
         html_header("CVS log for $where");          html_header("CVS log for $where");
Line 2030  sub doLog {
Line 2334  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 <<EOF;          print &link('Request diff between arbitrary revisions', '#diff');
 <A HREF="#diff">Request diff between arbitrary revisions</A>          print '<HR NOSHADE>';
 <HR NOSHADE>  
 EOF  
         if ($curbranch) {          if ($curbranch) {
             print "Default branch: ", ($revsym{$curbranch} || $curbranch);              print "Default branch: ", ($revsym{$curbranch} || $curbranch);
         }          }
Line 2062  EOF
Line 2365  EOF
         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) {
             print "<INPUT TYPE=HIDDEN NAME=\"$_\" VALUE=\"$input{$_}\">\n"              printf('<INPUT TYPE=HIDDEN NAME="%s" VALUE="%s">',  $_, $input{$_})
                 if (defined($input{$_})                  if (defined($input{$_})
                     && ($input{$_} ne $DEFAULTVALUE{$_} && $input{$_} ne ""));                      && ((!defined($DEFAULTVALUE{$_})
                            || $input{$_} ne $DEFAULTVALUE{$_})
                           && $input{$_} ne ""));
         }          }
         print "Diffs between \n";          print "<TABLE><TR>\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'>\n";          print "<INPUT TYPE=\"TEXT\" SIZE=\"$inputTextSize\" NAME=\"tr1\" VALUE=\"$diffrev\" onChange='document.diff_select.r1.selectedIndex=0'></TD>";
         print " and \n";          print "<TD><BR></TD></TR>\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='docuement.diff_select.r2.selectedIndex=0'>\n";          print "<INPUT TYPE=\"TEXT\" SIZE=\"$inputTextSize\" NAME=\"tr2\" VALUE=\"$diffrev\" onChange='document.diff_select.r2.selectedIndex=0'></TD>";
         print "<BR>Type of Diff should be a&nbsp;";          print "<TD><INPUT TYPE=SUBMIT VALUE=\"  Get Diffs  \"></TD>\n";
         printDiffSelect();  
         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 2111  EOF
Line 2416  EOF
                         && $input{"only_with_tag"} eq $_);                          && $input{"only_with_tag"} eq $_);
                 print ">${_}\n";                  print ">${_}\n";
             }              }
             print "</SELECT>\n";              print "</SELECT></TD><TD></TD></TR>\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{$_}) && $input{$_} ne $DEFAULTVALUE{$_}                  if (defined($input{$_})
                       && (!defined($DEFAULTVALUE{$_})
                           || $input{$_} ne $DEFAULTVALUE{$_})
                     && $input{$_} ne "");                      && $input{$_} ne "");
         }          }
         print "Sort log by: \n";          print "<TR><TD align=right>";
         print "<SELECT NAME=\"logsort\"";          print "<A name=logsort></A>\n";
         print " onchange=\"submit()\"" if ($use_java_script);          print "Sort log by:</TD>";
         print ">\n";          print "<TD>";
         print "<OPTION VALUE=cvs",$logsort eq "cvs" ? " SELECTED" : "", ">Not sorted";          printLogSortSelect($use_java_script);
         print "<OPTION VALUE=date",$logsort eq "date" ? " SELECTED" : "", ">Commit date";          print "</TD>";
         print "<OPTION VALUE=rev",$logsort eq "rev" ? " SELECTED" : "", ">Revision";          print "<TD><INPUT TYPE=SUBMIT VALUE=\"  Set  \"></TD>";
         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";
 }  }
   
 sub flush_diff_rows ($$$$)  sub flush_diff_rows($$$$) {
 {  
     my $j;      my $j;
     my ($leftColRef,$rightColRef,$leftRow,$rightRow) = @_;      my ($leftColRef,$rightColRef,$leftRow,$rightRow) = @_;
     if ($state eq "PreChangeRemove") {          # we just got remove-lines before      if ($state eq "PreChangeRemove") {          # we just got remove-lines before
Line 2174  sub flush_diff_rows ($$$$)
Line 2476  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 ($i,$difftxt, $where_nd, $filename, $pathname, $scriptwhere_nd);    my ($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 2184  sub human_readable_diff($){
Line 2486  sub human_readable_diff($){
   ($pathname = $where_nd) =~ s/(Attic\/)?[^\/]*$//;    ($pathname = $where_nd) =~ s/(Attic\/)?[^\/]*$//;
   ($scriptwhere_nd = $scriptwhere) =~ s/.diff$//;    ($scriptwhere_nd = $scriptwhere) =~ s/.diff$//;
   
   navigateHeader ($scriptwhere_nd, $pathname, $filename, $rev, "diff");    navigateHeader($scriptwhere_nd, $pathname, $filename, $rev, "diff");
   
   # Read header to pick up read revision and date, if possible    # Read header to pick up read revision and date, if possible
   while (<$fh>) {    while (<$fh>) {
Line 2200  sub human_readable_diff($){
Line 2502  sub human_readable_diff($){
     $rev2 = $r2r;      $rev2 = $r2r;
     $date2 = $r2d;      $date2 = $r2d;
   }    }
   
   print "<h3 align=center>Diff for /$where_nd between version $rev1 and $rev2</h3>\n";  
   
   print "<table border=0 cellspacing=0 cellpadding=0 width=\"100%\">\n";    print "<h3 align=center>Diff for /$where_nd between version $rev1 and $rev2</h3>\n",
   print "<tr bgcolor=\"#ffffff\">\n";      "<table border=0 cellspacing=0 cellpadding=0 width=\"100%\">\n",
   print "<th width=\"50%\" valign=TOP>";        "<tr bgcolor=\"#ffffff\">\n",
   print "version $rev1";          "<th width=\"50%\" valign=TOP>",
             "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",
   print "<th width=\"50%\" valign=TOP>";      "<th width=\"50%\" valign=TOP>",
   print "version $rev2";        "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 2228  sub human_readable_diff($){
Line 2529  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 2246  sub human_readable_diff($){
Line 2553  sub human_readable_diff($){
       }        }
       else {        else {
           ($diffcode,$rest) = $difftxt =~ /^([-+ ])(.*)/;            ($diffcode,$rest) = $difftxt =~ /^([-+ ])(.*)/;
           $_ = spacedHtmlText ($rest);            $_ = spacedHtmlText($rest, $d{'tabstop'});
   
           # Add fontface, size            # Add fontface, size
           $_ = "$fs&nbsp;$_$fe";            $_ = "$fs&nbsp;$_$fe";
   
           #########            #########
           # little state machine to parse unified-diff output (Hen, zeller@think.de)            # little state machine to parse unified-diff output (Hen, zeller@think.de)
           # in order to get some nice 'ediff'-mode output            # in order to get some nice 'ediff'-mode output
Line 2268  sub human_readable_diff($){
Line 2575  sub human_readable_diff($){
                   $state = "PreChange";                    $state = "PreChange";
                   $rightCol[$rightRow++] = $_;                    $rightCol[$rightRow++] = $_;
               }                }
           }            }
           elsif ($diffcode eq '-') {            elsif ($diffcode eq '-') {
               $state = "PreChangeRemove";                $state = "PreChangeRemove";
               $leftCol[$leftRow++] = $_;                $leftCol[$leftRow++] = $_;
Line 2324  sub human_readable_diff($){
Line 2631  sub human_readable_diff($){
   print "</tr></table>";    print "</tr></table>";
 }  }
   
 sub navigateHeader ($$$$$) {  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 "<\!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">";
     print "<HTML>\n<HEAD>\n";      print "<HTML>\n<HEAD>\n";
     print '<!-- CVSweb $zRevision: 1.93 $  $Revision$ -->';      print '<!-- CVSweb $zRevision: 1.103 $  $Revision$ -->';
     print "\n<TITLE>$path$filename - $title - $rev</TITLE></HEAD>\n";      print "\n<TITLE>$path$filename - $title - $rev</TITLE></HEAD>\n";
     print  "<BODY BGCOLOR=\"$backcolor\">\n";      print  "$body_tag_for_src\n";
     print "<table width=\"100%\" border=0 cellspacing=0 cellpadding=1 bgcolor=\"$navigationHeaderColor\">";      print "<table width=\"100%\" border=0 cellspacing=0 cellpadding=1 bgcolor=\"$navigationHeaderColor\">";
     print "<tr valign=bottom><td>";      print "<tr valign=bottom><td>";
     print  "<a href=\"$swhere$query#rev$rev\">$backicon";      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>";
   
     print "<td align=right>$diricon <b>Up to ", &clickablePath($path, 1), "</b></td>";      print "<td align=right>$diricon <b>Up to ", &clickablePath($path, 1), "</b></td>";
     print "</tr></table>";      print "</tr></table>";
 }  }
   
 sub plural_write ($$)  sub plural_write($$) {
 {  
     my ($num,$text) = @_;      my ($num,$text) = @_;
     if ($num != 1) {      if ($num != 1) {
         $text = $text . "s";          $text .= "s";
     }      }
     if ($num > 0) {      if ($num > 0) {
         return $num . " " . $text;          return join(' ', $num, $text);
     }      }
     else {      else {
         return "";          return "";
Line 2362  sub plural_write ($$)
Line 2668  sub plural_write ($$)
 # '..time ago'  # '..time ago'
 # H. Zeller <zeller@think.de>  # H. Zeller <zeller@think.de>
 ##  ##
 sub readableTime ($$)  sub readableTime($$) {
 {  
     my ($i, $break, $retval);      my ($i, $break, $retval);
     my ($secs,$long) = @_;      my ($secs,$long) = @_;
   
Line 2381  sub readableTime ($$)
Line 2686  sub readableTime ($$)
                    31536000, 'year');                     31536000, 'year');
     my @breaks = sort {$a <=> $b} keys %desc;      my @breaks = sort {$a <=> $b} keys %desc;
     $i = 0;      $i = 0;
     while ($i <= $#breaks && $secs >= 2 * $breaks[$i]) {      while ($i <= $#breaks && $secs >= 2 * $breaks[$i]) {
         $i++;          $i++;
     }      }
     $i--;      $i--;
Line 2392  sub readableTime ($$)
Line 2697  sub readableTime ($$)
         my $rest = $secs % $break;          my $rest = $secs % $break;
         $i--;          $i--;
         $break = $breaks[$i];          $break = $breaks[$i];
         my $resttime = plural_write(int ($rest / $break),          my $resttime = plural_write(int ($rest / $break),
                                 $desc{$break});                                  $desc{$break});
         if ($resttime) {          if ($resttime) {
             $retval = $retval . ", " . $resttime;              $retval .= ", $resttime";
         }          }
     }      }
   
Line 2410  sub readableTime ($$)
Line 2715  sub readableTime ($$)
 # basename (last directory/file) is a link as well  # basename (last directory/file) is a link as well
 ##  ##
 sub clickablePath($$) {  sub clickablePath($$) {
     my ($pathname,$clickLast) = @_;      my ($pathname,$clickLast) = @_;
     my $retval = '';      my $retval = '';
   
     if ($pathname eq '/') {      if ($pathname eq '/') {
         # this should never happen - chooseCVSRoot() is          # this should never happen - chooseCVSRoot() is
         # intended to do this          # intended to do this
         $retval = "[$cvstree]";          $retval = "[$cvstree]";
     }      }
     else {      else {
         $retval = $retval . " <a href=\"${scriptname}/${query}#dirlist\">[$cvstree]</a>";          $retval .= ' ' . &link("[$cvstree]", sprintf('%s/%s#dirlist',
                                                        $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 = $retval . "<a href=\"${scriptname}"                  $retval .= &link($_, join('',
                     . urlencode($wherepath)                                            $scriptname,
                     . (!$last || $lastslash ? '/' : '')                                            urlencode($wherepath),
                     . ${query}                                            (!$last || $lastslash ? '/' : ''),
                     . (!$last || $lastslash ? "#dirlist" : "")                                            $query,
                     . "\">$_</a>";                                            (!$last || $lastslash ? "#dirlist" : "")));
             }              }
             else { # do not make a link to the current dir              else { # do not make a link to the current dir
                 $retval = $retval .  $_;                  $retval .= $_;
             }              }
         }          }
     }      }
Line 2454  sub chooseCVSRoot() {
Line 2761  sub chooseCVSRoot() {
         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) {
             print "<input type=hidden NAME=$k VALUE=$input{$k}>\n"              print "<input type=hidden NAME=$k VALUE=$input{$k}>\n"
                 if ($input{$k}) && ($k ne "cvsroot");                  if ($input{$k}) && ($k ne "cvsroot");
         }          }
         # Form-Elements look wierd in Netscape if the background          # Form-Elements look wierd in Netscape if the background
Line 2468  sub chooseCVSRoot() {
Line 2775  sub chooseCVSRoot() {
         foreach $k (@foo) {          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} :              print ">", ($CVSROOTdescr{$k} ? $CVSROOTdescr{$k} : $k), "</option>\n";
                         $k). "</option>\n";  
         }          }
         print "</select>\n</td>";          print "</select>\n</td>";
         print "<td><input type=submit value=\"Go\"></td>";          print "<td><input type=submit value=\"Go\"></td>";
Line 2488  sub chooseMirror() {
Line 2794  sub chooseMirror() {
     # and may not be useful for your site; If you don't      # and may not be useful for your site; If you don't
     # set %MIRRORS this won't show up, anyway      # set %MIRRORS this won't show up, anyway
     #      #
     # Should perhaps exlude the current site somehow..      # Should perhaps exlude the current site somehow..
     if (keys %MIRRORS) {      if (keys %MIRRORS) {
         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 qq(<a href="$MIRRORS{$mirror}">$mirror</A>\n);              print &link(htmlquote($mirror),$MIRRORS{$mirror});
             $moremirrors = 1;              $moremirrors = 1;
         }          }
         print "<p>\n";          print "<p>\n";
     }      }
 }  }
   
 sub fileSortCmp {  sub fileSortCmp() {
     my ($comp) = 0;      my ($comp) = 0;
     my ($c,$d,$af,$bf);      my ($c,$d,$af,$bf);
   
Line 2531  sub fileSortCmp {
Line 2837  sub fileSortCmp {
 }  }
   
 # make A url for downloading  # make A url for downloading
 sub download_url {  sub download_url($$;$) {
     my ($url,$revision,$mimetype) = @_;      my ($url,$revision,$mimetype) = @_;
   
     $revision =~ s/\b0\.//;      $revision =~ s/\b0\.//;
   
     if (defined($checkout_magic)      if (defined($checkoutMagic)
         && (!defined($mimetype) || $mimetype ne "text/x-cvsweb-markup")) {          && (!defined($mimetype) || $mimetype ne "text/x-cvsweb-markup")) {
         my ($path);          my $path = $where;
         ($path = $where) =~ s|/[^/]*$|/|;          $path =~ s|/[^/]*$|/|;
         $url = "$scriptname/$checkoutMagic/${path}$url";          $url = "$scriptname/$checkoutMagic/${path}$url";
     }      }
     $url .= "?rev=$revision";      $url .= "?rev=$revision";
     $url .= "&amp;content-type=$mimetype" if (defined($mimetype));      $url .= '&content-type=' . urlencode($mimetype) if (defined($mimetype));
   
     return $url;      $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 =~ /^\(/;  
     $textlink =~ s/^\(// if ($paren);      printf '<A HREF="%s"', htmlquote("$fullurl$barequery");
     $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 2580  sub download_link {
Line 2882  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) {
             print " onClick=\"window.open('$fullurl','cvs_checkout',";              my @attr = qw(resizeable scrollbars);
             print "'resizeable,scrollbars";  
             print ",status,toolbar" if (defined($mimetype)              push @attr, qw(status toolbar)
                 && $mimetype eq "text/html");                if (defined($mimetype) && $mimetype eq "text/html");
             print ",width=$extern_window_width" if (defined($extern_window_width));  
             print ",height=$extern_window_height" if (defined($extern_window_height));              push @attr, "width=$extern_window_width"
             print"');\"";                if (defined($extern_window_width));
   
               push @attr, "height=$extern_window_height"
                 if (defined($extern_window_height));
   
               printf q` onClick="window.open('%s','cvs_checkout','%s');"`,
                 htmlquote($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 2612  sub toggleQuery($$) {
Line 2919  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 .= "&amp;" if ($newquery ne "");              $newquery .= "&" if ($newquery ne "");
             $newquery .= urlencode($var) . "=" . urlencode($value);              $newquery .= urlencode($var) . "=" . urlencode($value);
         }          }
     }      }
Line 2622  sub toggleQuery($$) {
Line 2929  sub toggleQuery($$) {
     return "";      return "";
 }  }
   
 sub urlencode {  sub urlencode($) {
     my ($in) = @_;      local($_) = @_;
     my ($out);  
     ($out = $in) =~ s/([\000-+{-\377])/sprintf("%%%02x", ord($1))/ge;      s/[\000-+{-\377]/sprintf("%%%02x", ord($&))/ge;
     return $out;  
   
          $_;
 }  }
   
 sub http_header {  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 http_header(;$) {
     my $content_type = shift || "text/html";      my $content_type = shift || "text/html";
     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 2646  sub http_header {
Line 2979  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) {
         my $fh = do {local(*FH);};          if ($has_zlib || (defined($GZIPBIN) && open(GZIP, "|$GZIPBIN -1 -c"))) {
         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 2659  sub http_header {
Line 2991  sub http_header {
                     print "\r\n"; # Close headers                      print "\r\n"; # Close headers
             }              }
             $| = 1; $| = 0; # Flush header output              $| = 1; $| = 0; # Flush header output
             select ($fh);              if ($has_zlib) {
                   tie *GZIP, __PACKAGE__, \*STDOUT;
               }
               select(GZIP);
               $gzip_open = 1;
 #           print "<!-- gzipped -->" if ($content_type eq "text/html");  #           print "<!-- gzipped -->" if ($content_type eq "text/html");
         }          }
         else {          else {
Line 2684  sub http_header {
Line 3020  sub http_header {
   
 sub html_header($) {  sub html_header($) {
     my ($title) = @_;      my ($title) = @_;
     my $version = '$zRevision: 1.93 $  $Revision$';      my $version = '$zRevision: 1.103 $  $Revision$'; #'
     http_header();      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"
Line 2698  $logo <h1 align="center">$title</h1>
Line 3034  $logo <h1 align="center">$title</h1>
 EOH  EOH
 }  }
   
 sub html_footer {  sub html_footer() {
     return "<hr noshade><address>$address</address>\n";      return "<hr noshade><address>$address</address>\n";
 }  }
   
 sub link_tags  sub link_tags($) {
 {  
     my ($tags) = @_;      my ($tags) = @_;
     my ($ret) = "";      my ($ret) = "";
     my ($fileurl,$filename);      my ($fileurl,$filename);
Line 2713  sub link_tags
Line 3048  sub link_tags
   
     foreach my $sym (split(", ", $tags)) {      foreach my $sym (split(", ", $tags)) {
         $ret .= ",\n" if ($ret ne "");          $ret .= ",\n" if ($ret ne "");
         $ret .= "<A HREF=\"$fileurl"          $ret .= &link($sym, $fileurl . toggleQuery('only_with_tag',$sym));
                 . toggleQuery('only_with_tag',$sym) . "\">$sym</A>";  
     }      }
     return $ret."\n";      return "$ret\n";
 }  }
   
 #  #
 # See if a module is listed in the config file's @HideModule list.  # See if a module is listed in the config file's @HideModule list.
 #  #
 sub forbidden_module {  sub forbidden_module($) {
     my($module) = @_;      my($module) = @_;
   
     for (my $i=0; $i < @HideModules; $i++) {      for (my $i=0; $i < @HideModules; $i++) {
         return 1 if $module eq $HideModules[$i];          return 1 if $module eq $HideModules[$i];
     }      }
   
     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.3.9  
changed lines
  Added in v.3.37

CVSweb