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

Diff for /cvsweb/cvsweb.cgi between version 4.18 and 4.39

version 4.18, 2019/11/11 14:37:54 version 4.39, 2019/11/29 23:42:40
Line 55  use filetest qw(access);
Line 55  use filetest qw(access);
   
 use vars qw (  use vars qw (
   $VERSION $CheckoutMagic $MimeTypes $DEBUG    $VERSION $CheckoutMagic $MimeTypes $DEBUG
   $config $allow_version_select    @CVSrepositories @CVSROOT %CVSROOT %CVSROOTdescr %DEFAULTVALUE %MTYPES
   @CVSrepositories @CVSROOT %CVSROOT %CVSROOTdescr    @DIFFTYPES %DIFFTYPES @LOGSORTKEYS %LOGSORTKEYS
   %MIRRORS %DEFAULTVALUE %ICONS %MTYPES  
   %DIFF_COMMANDS @DIFFTYPES %DIFFTYPES @LOGSORTKEYS %LOGSORTKEYS  
   %alltags %fileinfo %tags @branchnames %nameprinted    %alltags %fileinfo %tags @branchnames %nameprinted
   %symrev %revsym @allrevisions %date %author @revdisplayorder    %symrev %revsym @allrevisions %date %author @revdisplayorder
   @revisions %state %difflines %log %branchpoint @revorder $keywordsubstitution    @revisions %state %difflines %log %branchpoint @revorder $keywordsubstitution
   $prcgi @prcategories $re_prcategories $prkeyword $re_prkeyword $mancgi    $mancgi $doCheckout $scriptname $scriptwhere
   $doCheckout $scriptname $scriptwhere  
   $where $Browser $nofilelinks $maycompress @stickyvars    $where $Browser $nofilelinks $maycompress @stickyvars
   $is_links $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 $defaultDiffType $logsort $cvstree $cvsroot    $bylog $byfile $defaultDiffType $logsort $cvstree $cvsroot
   $charset $output_filter    %CMD $allow_compress $backicon $diricon $fileicon
   @command_path %CMD $allow_compress $backicon $diricon $fileicon    $fullname $logo $defaulttitle $address $binfileicon $iconsdir
   $fullname $cvstreedefault $logo $defaulttitle $address $binfileicon    $shortLogLen $show_author $hr_breakable $hr_ignwhite $hr_ignkeysubst
   $long_intro $short_instruction $shortLogLen $show_author    $mime_types $allow_annotate $allow_markup $allow_mailtos
   $tablepadding $hr_breakable $showfunc $hr_ignwhite $hr_ignkeysubst  
   $inputTextSize $mime_types $allow_annotate $allow_markup $allow_mailtos  
   $allow_log_extra $allow_dir_extra $allow_source_extra    $allow_log_extra $allow_dir_extra $allow_source_extra
   $edit_option_form    $edit_option_form
   $show_subdir_lastmod $show_log_in_markup $preformat_in_markup    $show_subdir_lastmod $show_log_in_markup $preformat_in_markup
   $tabstop $state $annTable $sel @ForbiddenFiles    $tabstop $state $annTable $sel @ForbiddenFiles
   $use_descriptions %descriptions @mytz $dwhere    $use_descriptions %descriptions $dwhere
   $use_moddate $gzip_open $file_list_len    $use_moddate $gzip_open $file_list_len
   $allow_tar @tar_options @gzip_options @zip_options @cvs_options    $allow_tar @tar_options @cvs_options
   @annotate_options @rcsdiff_options    @annotate_options @rcsdiff_options
   $HTML_DOCTYPE $HTML_META $cssurl $CSS $cvshistory_url    $HTML_DOCTYPE $HTML_META $cssurl $CSS
   $allow_enscript @enscript_options %enscript_types  
 );  );
   
 require Compress::Zlib;  require Compress::Zlib;
Line 91  use Cwd                   qw(abs_path);
Line 85  use Cwd                   qw(abs_path);
 use File::Path            qw(rmtree);  use File::Path            qw(rmtree);
 use File::Spec::Functions qw(canonpath catdir catfile curdir devnull rootdir  use File::Spec::Functions qw(canonpath catdir catfile curdir devnull rootdir
                              tmpdir updir);                               tmpdir updir);
 use File::Temp            qw(tempdir tempfile);  use File::Temp            qw(tempdir);
 use IPC::Run              qw();  use IPC::Run              qw();
 use Time::Local           qw(timegm);  use Time::Local           qw(timegm);
 use URI::Escape           qw(uri_escape uri_unescape);  use URI::Escape           qw(uri_escape uri_unescape);
Line 146  sub spacedHtmlText($;$);
Line 140  sub spacedHtmlText($;$);
 sub link($$);  sub link($$);
 sub revcmp($$);  sub revcmp($$);
 sub fatal($$@);  sub fatal($$@);
 sub config_error($$);  
 sub redirect($;$);  sub redirect($;$);
 sub safeglob($);  sub safeglob($);
 sub search_path($);  sub search_path($);
 sub getEnscriptHL($);  
 sub getMimeType($;$);  sub getMimeType($;$);
 sub head($;$);  sub head($;$);
 sub scan_directives(@);  sub scan_directives(@);
 sub openOutputFilter();  
 sub doAnnotate($$);  sub doAnnotate($$);
 sub doCheckout($$$);  sub doCheckout($$$);
 sub doEnscript($$$;$);  
 sub cvswebMarkup($$$$$$;$);  sub cvswebMarkup($$$$$$;$);
 sub viewable($);  sub viewable($);
 sub doDiff($$$$$$);  sub doDiff($$$$$$);
Line 172  sub plural_write($$);
Line 162  sub plural_write($$);
 sub readableTime($$);  sub readableTime($$);
 sub clickablePath($$);  sub clickablePath($$);
 sub chooseCVSRoot();  sub chooseCVSRoot();
 sub chooseMirror();  
 sub fileSortCmp();  sub fileSortCmp();
 sub download_url($$;$);  sub download_url($$;$);
 sub download_link($$$;$);  sub download_link($$$;$);
 sub display_url($$;$);  sub display_url($$;$);
 sub display_link($$;$$);  sub display_link($$;$$);
 sub history_link($$;$);  
 sub toggleQuery($;$);  sub toggleQuery($;$);
 sub htmlquote($);  sub htmlquote($);
 sub htmlunquote($);  sub htmlunquote($);
Line 190  sub link_tags($);
Line 178  sub link_tags($);
 sub forbidden($);  sub forbidden($);
 sub startproc(@);  sub startproc(@);
 sub runproc(@);  sub runproc(@);
 sub checkout_to_temp($$$);  
   
 # Get rid of unsafe environment vars.  Don't do this in the BEGIN block  # Get rid of unsafe environment vars.  Don't do this in the BEGIN block
 # (think mod_perl)...  # (think mod_perl)...
 delete(@ENV{qw(PATH IFS CDPATH ENV BASH_ENV)});  delete(@ENV{qw(PATH IFS CDPATH ENV BASH_ENV)});
   
 # Location of the configuration file inside the web server chroot:  # Helps to achieve read only access to the repositories
 $config = '/conf/cvsweb/cvsweb.conf';  # with cvs >= 1.12.1 and doesn't hurt other versions.
   $ENV{CVSREADONLYFS} = 1;
   
 ######## Configuration parameters #########  ######## configuration defaults #########
   
 @CVSrepositories = @CVSROOT = %CVSROOT = %MIRRORS = %DEFAULTVALUE = %ICONS =  %CMD = (
   %MTYPES = %tags = %alltags = %fileinfo = %DIFF_COMMANDS = ();    cvs     => '/usr/bin/cvs',
     rcsdiff => '/usr/bin/rcsdiff',
     rlog    => '/usr/bin/rlog',
     tar     => '/bin/tar',
   );
   
 $cvstreedefault = $logo = $defaulttitle =  %DEFAULTVALUE = (
   $address = $long_intro = $short_instruction = $shortLogLen = $show_author =    f               => 'u',
   $tablepadding = $hr_breakable = $showfunc = $hr_ignwhite =    hideattic       => 1,
   $hr_ignkeysubst = $inputTextSize = $mime_types = $allow_annotate =    hidecvsroot     => 0,
   $allow_markup = $allow_compress = $edit_option_form =    hidenonreadable => 1,
   $show_subdir_lastmod = $show_log_in_markup = $preformat_in_markup =    ignorecase      => 0,
   $tabstop = $use_moddate = $gzip_open = $DEBUG =    ln              => 0,
   $cvshistory_url = $allow_tar = undef;    logsort         => 'date',
     sortby          => 'file',
   );
   
 $allow_version_select = $allow_mailtos = $allow_log_extra = 1;  @ForbiddenFiles = (
     qr|^CVSROOT/+passwd$|o, # CVSROOT/passwd should not be 'cvs add'ed though.
     qr|/\.cvspass$|o,       # Ditto.  Just in case.
   );
   
   %MTYPES = (
     gif  => 'image/gif',
     html => 'text/html',
     jpeg => 'image/jpeg',
     jpg  => 'image/jpeg',
     png  => 'image/png',
   );
   
   $address = 'CVSweb';
   $allow_annotate = 1;
   $allow_compress = 0;
   $allow_dir_extra = 1;
   $allow_log_extra = 1;
   $allow_mailtos = 1;
   $allow_markup = 1;
   $allow_source_extra = 1;
   $allow_tar = 0;
   @annotate_options = qw(-f -R);
   $cssurl = '/css/cvsweb.css';
   @cvs_options = qw(-f -R);
   @CVSrepositories = ('local', ['Local Repository', '/cvs']);
   $DEBUG = 0;
   $defaulttitle = 'CVS Repository';
   $edit_option_form = 1;
   $file_list_len = 0;
   $hr_breakable = 1;
   $hr_ignkeysubst = 1;
   $hr_ignwhite = 0;
   $iconsdir = '/icons';
   $logo = undef;
   $mancgi = 'https://man.openbsd.org/%s.%s';
   $mime_types = '/conf/mime.types';
   $preformat_in_markup = 0;
   @rcsdiff_options = qw(-q);
   $shortLogLen = 80;
   $show_author = 1;
   $show_log_in_markup = 1;
   $show_subdir_lastmod = 0;
   $tabstop = 8;
   @tar_options = qw();
   $use_descriptions = 0;
   $use_moddate = 1;
   
   ######## load configuration #########
   
   {
     my $config = '/conf/cvsweb/cvsweb.conf';
     last unless -e $config;
     defined do $config and last;
     $@ and fatal '500 Internal Error',
       'Error loading configuration file "<code>%s</code>": <pre>%s</pre>',
       $config, $@;
     fatal '500 Internal Error',
       'Cannot read configuration file "<code>%s</code>": %s',
       $config, $! || 'unknown error';
   }
   
   ######## other global variables #########
   
 @DIFFTYPES = qw(h H u c);  @DIFFTYPES = qw(h H u c);
 @DIFFTYPES{@DIFFTYPES} = (  @DIFFTYPES{@DIFFTYPES} = (
   {    {
Line 246  $allow_version_select = $allow_mailtos = $allow_log_ex
Line 302  $allow_version_select = $allow_mailtos = $allow_log_ex
   { descr => 'Revision',    },    { descr => 'Revision',    },
 );  );
   
 ##### End of configuration parameters #####  %alltags = ();
   @CVSROOT = ();
   %CVSROOT = ();
   %CVSROOTdescr = ();
   %fileinfo = ();
   $gzip_open = 0;
   %tags = ();
   
   ######## end of global variables #########
   
 my $pathinfo = '';  my $pathinfo = '';
 if (defined($ENV{PATH_INFO}) && $ENV{PATH_INFO} ne '') {  if (defined($ENV{PATH_INFO}) && $ENV{PATH_INFO} ne '') {
   ($pathinfo) = ($ENV{PATH_INFO} =~ VALID_PATH)    ($pathinfo) = ($ENV{PATH_INFO} =~ VALID_PATH)
Line 267  $scriptname    = '' unless defined($scriptname);
Line 331  $scriptname    = '' unless defined($scriptname);
 $where         =  $pathinfo;  $where         =  $pathinfo;
 $doCheckout    =  $where =~ s|^/$CheckoutMagic/|/|o;  $doCheckout    =  $where =~ s|^/$CheckoutMagic/|/|o;
 $where         =~ s|^/||;  $where         =~ s|^/||;
 $scriptname    =~ s|^/*|/|;  $scriptname    =~ s|^/+||;
   
 # Let's workaround thttpd's stupidity..  # Let's workaround thttpd's stupidity..
 if ($scriptname =~ m|/$|) {  if ($scriptname =~ m|/$|) {
Line 275  if ($scriptname =~ m|/$|) {
Line 339  if ($scriptname =~ m|/$|) {
   my $re = quotemeta $pathinfo;    my $re = quotemeta $pathinfo;
   $scriptname =~ s/$re$//;    $scriptname =~ s/$re$//;
 }  }
   $scriptname = "/$scriptname" if $scriptname;
   
 # $scriptname : the URI escaped path to this script  # $scriptname : the URI escaped path to this script
 # $where      : the path in the CVS repository (without leading /, or only /)  # $where      : the path in the CVS repository (without leading /, or only /)
Line 319  $maycompress = (
Line 384  $maycompress = (
   qw(cvsroot hideattic ignorecase sortby logsort f only_with_tag ln    qw(cvsroot hideattic ignorecase sortby logsort f only_with_tag ln
      hidecvsroot hidenonreadable);       hidecvsroot hidenonreadable);
   
 #  
 # Load configuration.  
 #  
 if (-f $config) {  
   do "$config" or config_error($config, $@);  
 } else {  
   fatal("500 Internal Error",  
         'Configuration not found.  Set the parameter <code>$config</code> in cvsweb.cgi to your <b>cvsweb.conf</b> configuration file first.');  
 }  
   
 # Try to find a readable dir where we can cd into.  Some abs_path()  # Try to find a readable dir where we can cd into.  Some abs_path()
 # implementations as well as various cvs operations require such a dir to  # implementations as well as various cvs operations require such a dir to
 # work properly.  # work properly.
Line 473  for (my $i = 0; $i < scalar(@CVSrepositories); $i += 2
Line 528  for (my $i = 0; $i < scalar(@CVSrepositories); $i += 2
     next;      next;
   }    }
   $rootfound ||= 1;    $rootfound ||= 1;
   $cvstreedefault = $key unless defined($cvstreedefault);  
   $CVSROOTdescr{$key} = $descr;    $CVSROOTdescr{$key} = $descr;
   $CVSROOT{$key} = $root;    $CVSROOT{$key} = $root;
   push(@CVSROOT, $key);    push(@CVSROOT, $key);
 }  }
 unless ($rootfound) {  unless ($rootfound) {
   fatal('500 Internal Error',    fatal('500 Internal Error', 'no valid CVS roots found');
         'No valid CVS roots found!  See <code>@CVSrepositories</code> in ' .  
         'the configuration file (<code>%s</code>).',  
         $config);  
 }  }
 undef $rootfound;  undef $rootfound;
   
 #  $DEFAULTVALUE{cvsroot} = $CVSrepositories[0];
 # Default CVS root  
 #  
 if (!defined($CVSROOT{$cvstreedefault})) {  
   fatal("500 Internal Error",  
         '<code>$cvstreedefault</code> points to a repository (%s) not ' .  
         'defined in <code>@CVSrepositories</code> in your configuration ' .  
         'file (<code>%s</code>).',  
         $cvstreedefault,  
         $config);  
 }  
   
 $DEFAULTVALUE{cvsroot} = $cvstreedefault;  
   
 while (my ($key, $defval) = each %DEFAULTVALUE) {  while (my ($key, $defval) = each %DEFAULTVALUE) {
   
   # Replace not given parameters with defaults.    # Replace not given parameters with defaults.
Line 534  foreach (@stickyvars) {
Line 573  foreach (@stickyvars) {
   }    }
 }  }
   
 if ($allow_enscript) {  
   push(@DIFFTYPES, qw(uc cc));  
   @DIFFTYPES{qw(uc cc)} = (  
     {  
      'descr'   => 'unified, colored',  
      'opts'    => ['-u'],  
      'colored' => 0,  
     },  
     {  
      'descr'   => 'context, colored',  
      'opts'    => ['-c'],  
      'colored' => 0,  
     },  
   );  
 } else {  
   # No Enscript -> respect difftype, but don't offer colorization.  
   if ($input{f} && $input{f} =~ /^([ucs])c$/) {  
     $input{f} = $1;  
   }  
 }  
   
 # is there any query ?  # is there any query ?
 if (@barequery) {  if (@barequery) {
   $barequery = join (';', @barequery);    $barequery = join (';', @barequery);
Line 598  $logsort = $input{logsort};
Line 616  $logsort = $input{logsort};
 if ($input{cvsroot} && $CVSROOT{$input{cvsroot}}) {  if ($input{cvsroot} && $CVSROOT{$input{cvsroot}}) {
   $cvstree = $input{cvsroot};    $cvstree = $input{cvsroot};
 } else {  } else {
   $cvstree = $cvstreedefault;    $cvstree = $CVSrepositories[0];
 }  }
   
 $cvsroot = $CVSROOT{$cvstree};  $cvsroot = $CVSROOT{$cvstree};
   
 # create icons out of description  if ($iconsdir) {
 foreach my $k (keys %ICONS) {    $backicon = '<img src="' . $iconsdir . '/back.gif" alt="[BACK]"' .
   my ($itxt, $ipath, $iwidth, $iheight) = @{$ICONS{$k}};      ' border="0" width="20" height="22"/>';
   no strict 'refs';    $diricon = '<img src="' . $iconsdir . '/dir.gif" alt="[DIR]"' .
   if ($ipath) {      ' border="0" width="20" height="22"/>';
     ${"${k}icon"} =    $fileicon = '<img src="' . $iconsdir . '/text.gif" alt="[TXT]"' .
       sprintf('<img src="%s" alt="%s" border="0" width="%d" height="%d" />',      ' border="0" width="20" height="22"/>';
               htmlquote($ipath), htmlquote($itxt), $iwidth, $iheight);    $binfileicon = '<img src="' . $iconsdir . '/binary.gif" alt="[BIN]"' .
   } else {      ' border="0" width="20" height="22"/>';
     ${"${k}icon"} = $itxt;  } else {
   }    $backicon = 'back';
     $diricon = 'dir';
     $fileicon = 'file';
     $binfileicon = 'binfile';
 }  }
   
 my $config_cvstree = "$config-$cvstree";  
   
 # Do some special configuration for cvstrees  
 if (-f $config_cvstree) {  
   do "$config_cvstree"  
     or fatal("500 Internal Error",  
              'Error in loading configuration file: %s<br /><br />%s<br />',  
              $config_cvstree, $@);  
 }  
 undef $config_cvstree;  
   
 $re_prcategories  = '(?:' . join ('|', @prcategories) . ')' if @prcategories;  
 $re_prkeyword     = quotemeta($prkeyword) if defined($prkeyword);  
 $prcgi           .= '%s' if defined($prcgi) && $prcgi !~ /%s/;  
   
 $fullname         = catfile($cvsroot, $where);  $fullname         = catfile($cvsroot, $where);
   
 my $rewrite = 0;  my $rewrite = 0;
Line 676  if ($input{tarball}) {
Line 682  if ($input{tarball}) {
   
   my ($module)  =  ($where =~ m,^/?(.*),);    # untaint    my ($module)  =  ($where =~ m,^/?(.*),);    # untaint
   $module       =~ s,/([^/]*)$,,;    $module       =~ s,/([^/]*)$,,;
   my ($ext)     =  ($1 =~ /(\.t(?:ar\.)?gz|\.zip)$/);    my ($ext)     =  ($1 =~ /(\.t(?:ar\.)?gz)$/);
   my ($basedir) =  ($module =~ m,([^/]+)$,);    my ($basedir) =  ($module =~ m,([^/]+)$,);
   
   if ($basedir eq '' || $module eq '') {    if ($basedir eq '' || $module eq '') {
Line 684  if ($input{tarball}) {
Line 690  if ($input{tarball}) {
           'You cannot download the top level directory.');            'You cannot download the top level directory.');
   }    }
   
   my $istar = ($ext eq '.tar.gz' || $ext eq '.tgz');    unless ($ext eq '.tar.gz' || $ext eq '.tgz') {
   if ($istar) {      fatal('404 Not Found', 'Unsupported archive type.');
     fatal('500 Internal Error', 'tar command not found.') unless $CMD{tar};  
     fatal('500 Internal Error', 'gzip command not found.') unless $CMD{gzip};  
   }    }
   my $iszip = ($ext eq '.zip');  
   if ($iszip && !$CMD{zip}) {  
     fatal('500 Internal Error', 'zip command not found.');  
   }  
   if (!$istar && !$iszip) {  
     fatal('500 Internal Error', 'Unsupported archive type.');  
   }  
   
   my $tmpexportdir;    my $tmpexportdir;
   eval {    eval {
Line 726  if ($input{tarball}) {
Line 723  if ($input{tarball}) {
       ('500 Internal Error',        ('500 Internal Error',
        'Export failure (exit status %s), output: <pre>%s</pre>',         'Export failure (exit status %s), output: <pre>%s</pre>',
        $errcode, $err || $export_err);         $errcode, $err || $export_err);
   
   } else {    } else {
   
     $| = 1;    # Essential to get the buffering right.      $| = 1;    # Essential to get the buffering right.
     local (*TAR_OUT);      local (*TAR_OUT);
       my ($h, $err) = startproc($CMD{tar}, @tar_options, '-czf', '-',
     my (@cmd, $ctype);        $basedir, '>pipe', \*TAR_OUT);
     if ($istar) {  
       my @tar = ($CMD{tar}, @tar_options, '-cf', '-', $basedir);  
       my @gzip = ($CMD{gzip}, @gzip_options, '-c');  
       push(@cmd, \@tar, '|', \@gzip);  
       $ctype = 'application/x-gzip';  
     } elsif ($iszip) {  
       my @zip = ($CMD{zip}, @zip_options, '-r', '-', $basedir);  
       push(@cmd, \@zip, \'');  
       $ctype = 'application/zip';  
     }  
     push(@cmd, '>pipe', \*TAR_OUT);  
   
     my ($h, $err) = startproc(@cmd);  
     if ($h) {      if ($h) {
       print "Content-Type: $ctype\r\n\r\n";        print "Content-Type: application/x-gzip\r\n\r\n";
       local $/ = undef;        local $/ = undef;
       print <TAR_OUT>;        print <TAR_OUT>;
       $h->finish();        $h->finish();
     } else {      } else {
       @fatal = ('500 Internal Error',        @fatal = ('500 Internal Error',
                 '%s failure (exit status %s), output: <pre>%s</pre>',                  'tar failure (exit status %s), output: <pre>%s</pre>',
                 $istar ? 'Tar' : 'Zip', $? >> 8 || -1, $err);                  $? >> 8 || -1, $err);
     }      }
   }    }
   
Line 781  if (-d $fullname) {
Line 763  if (-d $fullname) {
   
   if ($where eq '/') {    if ($where eq '/') {
     html_header($defaulttitle);      html_header($defaulttitle);
     $long_intro =~ s/!!CVSROOTdescr!!/$CVSROOTdescr{$cvstree}/g;  
     print $long_intro;  
   } else {    } else {
     html_header($where);      html_header($where);
     my $html = (-f catfile($fullname, 'README.cvs.html,v') ||      my $html = (-f catfile($fullname, 'README.cvs.html,v') ||
Line 819  if (-d $fullname) {
Line 799  if (-d $fullname) {
       }        }
       $h->finish();        $h->finish();
     }      }
     print $short_instruction;  
   }    }
   
   if ($use_descriptions &&    if ($use_descriptions &&
Line 836  if (-d $fullname) {
Line 815  if (-d $fullname) {
   
   # give direct access to dirs    # give direct access to dirs
   if ($where eq '/') {    if ($where eq '/') {
     chooseMirror();  
     chooseCVSRoot();      chooseCVSRoot();
   
   } else {    } else {
     print '<p>Current directory: <b>', clickablePath($where, 0), '</b>';      print '<p>Current directory: <b>', clickablePath($where, 0), '</b>';
     if ($cvshistory_url) {  
       (my $d = $where) =~ s|^/*(.*?)/*$|$1|;  
       print ' - ', history_link($d, '');  
     }  
     print "</p>\n";      print "</p>\n";
     print "<p>Current tag: <b>", htmlquote($input{only_with_tag}), "</b></p>\n"      print "<p>Current tag: <b>", htmlquote($input{only_with_tag}), "</b></p>\n"
       if $input{only_with_tag};        if $input{only_with_tag};
Line 854  if (-d $fullname) {
Line 827  if (-d $fullname) {
   
   my $infocols = 1;    my $infocols = 1;
   
   printf(<<EOF, $tablepadding, 'Directory index of ' . htmlquote($where));    printf(<<EOF, 'Directory index of ' . htmlquote($where));
 <table class="dir" width="100%%" cellspacing="0" cellpadding="%s" summary="%s">  <table class="dir" width="100%%" cellspacing="0" cellpadding="2" summary="%s">
 <tr>  <tr>
 EOF  EOF
   printf('<th colspan="2"%s>', ($byfile ? ' class="sorted"' : ''));    printf('<th colspan="2"%s>', ($byfile ? ' class="sorted"' : ''));
Line 1149  EOF
Line 1122  EOF
   
   if ($allow_tar && $filesfound) {    if ($allow_tar && $filesfound) {
     my ($basefile) = ($where =~ m,(?:.*/)?([^/]+),);      my ($basefile) = ($where =~ m,(?:.*/)?([^/]+),);
     my $havetar = $CMD{tar} && $CMD{gzip};      if (defined($basefile) && $basefile ne '') {
     my $havezip = $CMD{zip};  
     if (defined($basefile) && $basefile ne '' && ($havetar || $havezip)) {  
       my $q = ($query ? "$query;" : '?') . 'tarball=1';        my $q = ($query ? "$query;" : '?') . 'tarball=1';
       print "<hr />\n",        print "<hr />\n",
         '<div style="text-align: center">Download this directory in ';          '<div style="text-align: center">Download this directory in ';
       # Mangle the filename so browsers show a reasonable filename to download.        # Mangle the filename so browsers show a reasonable filename to download.
       my @types = ();  
       $basefile = uri_escape($basefile);        $basefile = uri_escape($basefile);
       push(@types, &link('tarball', "$basefile.tar.gz$q")) if $havetar;        print &link('tarball', "$basefile.tar.gz$q");
       push(@types, &link('zip archive', "$basefile.zip$q")) if $havezip;        print "</div>\n";
       print join(' or ', @types), "</div>\n";  
     }      }
   }    }
   
Line 1176  EOF
Line 1145  EOF
       printf(qq{<input type="hidden" name="%s" value="%s" />\n},        printf(qq{<input type="hidden" name="%s" value="%s" />\n},
              $v, $input{$v} || 0);               $v, $input{$v} || 0);
     }      }
     if ($cvstree ne $cvstreedefault) {      if ($cvstree ne $CVSrepositories[0]) {
       print "<input type=\"hidden\" name=\"cvsroot\" value=\"$cvstree\" />\n";        print "<input type=\"hidden\" name=\"cvsroot\" value=\"$cvstree\" />\n";
     }      }
     print <<EOF;      print <<EOF;
Line 1478  sub htmlify($;$)
Line 1447  sub htmlify($;$)
   }    }
   
   if ($extra) {    if ($extra) {
   
     # get PR #'s as link: "PR#nnnn" "PR: nnnn, ..." "PR nnnn, ..." "bin/nnnn"  
     if (defined($prcgi) && defined($re_prkeyword)) {  
       my $prev;  
   
       do {  
         $prev = $_;  
         $_ = htmlify_sub {  
           s{  
             (\b$re_prkeyword[:\#]?\s*  
              (?:  
               \#?  
               \d+[,\s]\s*  
              )*  
              \#?)  
             (\d+)\b  
            }{  
              $1 . &link($2, sprintf($prcgi, $2))  
            }egix;  
         } $_;  
       } while ($_ ne $prev);  
   
       if (defined($re_prcategories)) {  
         $_ = htmlify_sub {  
           s{  
             (\b$re_prcategories/(\d+)\b)  
            }{  
              &link($1, sprintf($prcgi, $2))  
            }egox;  
         } $_;  
       }  
     }  
   
     # get manpage specs as link: "foo.1" "foo(1)"      # get manpage specs as link: "foo.1" "foo(1)"
     if (defined($mancgi)) {      if (defined($mancgi)) {
       $_ = htmlify_sub {        $_ = htmlify_sub {
Line 1607  sub fatal($$@)
Line 1543  sub fatal($$@)
   
   
 #  #
 # Signal a (fatal) configuration error.  
 #  
 sub config_error($$)  
 {  
   fatal('500 Internal Error',  
         'Error loading configuration file "<code>%s</code>":<br /><br />' .  
         '%s<br />', @_);  
 }  
   
   
 #  
 # Sends a redirect to the given URL.  # Sends a redirect to the given URL.
 #  #
 sub redirect($;$)  sub redirect($;$)
Line 1674  sub safeglob($)
Line 1599  sub safeglob($)
   
   
 #  #
 # Searches @command_path for the given executable file.  
 #  
 sub search_path($)  
 {  
   my ($command) = @_;  
   for my $d (@command_path) {  
     my $cmd = catfile($d, $command);  
     return $cmd if (-x $cmd && !-d _);  
   }  
   return '';  
 }  
   
   
 #  
 # Gets the enscript(1) highlight mode corresponding to the given filename,  
 # or undef if unsupported.  
 #  
 sub getEnscriptHL($)  
 {  
   return undef unless $allow_enscript;  
   my ($filename) = @_;  
   while (my ($hl, $regex) = each %enscript_types) {  
     return $hl if ($filename =~ $regex);  
   }  
   return undef;  
 }  
   
   
 #  
 # Gets the MIME type for the given file name.  # Gets the MIME type for the given file name.
 #  #
 sub getMimeType($;$)  sub getMimeType($;$)
Line 1775  sub scan_directives(@)
Line 1671  sub scan_directives(@)
 }  }
   
   
 sub openOutputFilter()  
 {  
   return unless $output_filter;  
   
   open(STDOUT, "|-") and return;  
   
   # child of child  
   open(STDERR, '>', devnull()) unless $DEBUG;  
   exec($output_filter) or exit -1;  
 }  
   
   
 ###############################  ###############################
 # show Annotation  # show Annotation
 ###############################  ###############################
Line 2130  EOF
Line 2014  EOF
     printf '<embed src="%s" width="100%%" height="100%%" /><br />',      printf '<embed src="%s" width="100%%" height="100%%" /><br />',
       $url . $barequery;        $url . $barequery;
   } else {    } else {
   
     print "<pre>\n";      print "<pre>\n";
     my $linenumbers = $input{ln} || 0;      my $linenumbers = $input{ln} || 0;
   
     if (my $enscript_hl = getEnscriptHL($filename)) {  
       doEnscript($filehandle, $enscript_hl, $linenumbers);  
   
     } else {  
       my $ln  = 0;        my $ln  = 0;
       my @buf = ();        my @buf = ();
       my $ts  = undef;        my $ts  = undef;
Line 2157  EOF
Line 2035  EOF
         }          }
         print $preformat_in_markup ? spacedHtmlText($_, $ts) : htmlquote($_);          print $preformat_in_markup ? spacedHtmlText($_, $ts) : htmlquote($_);
       }        }
     }  
   
     print "</pre>\n";      print "</pre>\n";
   }    }
   html_footer();    html_footer();
Line 2214  sub doDiff($$$$$$)
Line 2090  sub doDiff($$$$$$)
   
   my $mimetype = getMimeType($fullname);    my $mimetype = getMimeType($fullname);
   
   #  
   #  Check for per-MIME type diff commands.  
   #  
   my $diffcmd = undef;  
   if (my $diffcmds = $DIFF_COMMANDS{lc($mimetype)}) {  
     if ($f =~ /^ext(\d*)$/) {  
       my $n = $1 || 0;  
       $diffcmd = $diffcmds->[$n];  
     }  
   }  
   if ($diffcmd && $diffcmd->{cmd} && $diffcmd->{name}) {  
   
     if ($diffcmd->{args} && ref($diffcmd->{args}) ne 'ARRAY') {  
       fatal('500 Internal Error',  
             'Configuration error: arguments to external diff tools must ' .  
             'be given as array refs.  See "<code>%s</code>" in ' .  
             '<code>%%DIFF_COMMANDS</code>.',  
             $diffcmd->{name});  
     }  
   
     (my $cvsname = $where) =~ s/\.diff$//;  
   
     # Create two temporary files with the two revisions  
     my $temp_fn1 = checkout_to_temp($cvsroot, $cvsname, $rev1);  
     my $temp_fn2 = checkout_to_temp($cvsroot, $cvsname, $rev2);  
   
     # Execute chosen diff binary.  
     local (*DIFF_OUT);  
     my @cmd = ($diffcmd->{cmd});  
     push(@cmd, @{$diffcmd->{args}}) if $diffcmd->{args};  
     push(@cmd, $temp_fn1, $temp_fn2);  
     my ($h, $err) = startproc(\@cmd, \"", '>pipe', \*DIFF_OUT);  
     if (!$h) {  
       unlink($temp_fn1);  
       unlink($temp_fn2);  
       fatal('500 Internal Error',  
             'Diff failure (exit status %s), output: <pre>%s</pre>',  
             $? >> 8 || -1, $err);  
     }  
   
     http_header($diffcmd->{type} || 'text/plain');  
     local $/ = undef;  
     print <DIFF_OUT>;  
     $h->finish();  
     unlink($temp_fn1);  
     unlink($temp_fn2);  
   
     exit;  
   }  
   
   #  
   # Normal CVS diff.  
   #  
   
   $f = $DEFAULTVALUE{f} || 'u' if ($f =~ /^ext\d*$/);    $f = $DEFAULTVALUE{f} || 'u' if ($f =~ /^ext\d*$/);
   my $difftype = $DIFFTYPES{$f};    my $difftype = $DIFFTYPES{$f};
   if (!$difftype) {    if (!$difftype) {
Line 2278  sub doDiff($$$$$$)
Line 2100  sub doDiff($$$$$$)
   my $human_readable = $difftype->{colored};    my $human_readable = $difftype->{colored};
   
   # Apply special diff options.    # Apply special diff options.
   push @difftype, '-p' if $showfunc;    push @difftype, '-p';
   
   if ($human_readable) {    if ($human_readable) {
     push(@difftype, '-w')  if $hr_ignwhite;      push(@difftype, '-w')  if $hr_ignwhite;
Line 2288  sub doDiff($$$$$$)
Line 2110  sub doDiff($$$$$$)
   my $fh = do { local (*FH); };    my $fh = do { local (*FH); };
   if (!open($fh, "-|")) {    # child    if (!open($fh, "-|")) {    # child
     open(STDERR, ">&STDOUT");    # Redirect stderr to stdout      open(STDERR, ">&STDOUT");    # Redirect stderr to stdout
     openOutputFilter();  
     exec($CMD{rcsdiff}, @rcsdiff_options, @difftype, "-r$rev1", "-r$rev2",      exec($CMD{rcsdiff}, @rcsdiff_options, @difftype, "-r$rev1", "-r$rev2",
          $fullname) or exit -1;           $fullname) or exit -1;
   }    }
Line 2301  sub doDiff($$$$$$)
Line 2122  sub doDiff($$$$$$)
     html_footer();      html_footer();
     gzipclose();      gzipclose();
     exit;      exit;
   
   } elsif ($f =~ /^([ucs])c$/) {  
     #  
     # Enscript colored diff.  
     #  
     my $hl = 'diff';  
     $hl .= $1 if ($1 eq 'u' || $1 eq 's');  
     (my $where_nd = $where)       =~ s/\.diff$//;  
     (my $pathname = $where_nd)    =~ s|((?<=/)Attic/)?[^/]*$||;  
     (my $filename = $where_nd)    =~ s|^.*/||;  
     (my $swhere   = $scriptwhere) =~ s|\.diff$||;  
     navigateHeader($swhere, $pathname, $filename, $rev2, 'diff');  
     printf(<<EOF, $where_nd, $rev1, $rev2);  
 <h3 style="text-align: center">Diff for /%s between versions %s and %s</h3>  
 <pre>  
 EOF  
     doEnscript(\$fh, $hl, 0, 'cvsweb_diff');  
     print <<EOF;  
 </pre>  
 <hr style="width: 100%" />  
 <form method="get" action="$scriptwhere">  
 EOF  
     printDiffSelectStickyVars();  
     print 'Diff format: ';  
     printDiffSelect();  
     print "<input type=\"submit\" value=\"Show\" />\n</form>\n";  
     html_footer();  
     gzipclose();  
     exit;  
   
   } else {    } else {
     #      #
     # Plain diff.      # Plain diff.
Line 2425  sub getDirLogs($$@)
Line 2216  sub getDirLogs($$@)
   my $fh = do { local (*FH); };    my $fh = do { local (*FH); };
   if (!open($fh, '-|')) {                       # Child    if (!open($fh, '-|')) {                       # Child
     open(STDERR, '>', devnull()) unless $DEBUG; # Ignore rlog's complaints.      open(STDERR, '>', devnull()) unless $DEBUG; # Ignore rlog's complaints.
     openOutputFilter();  
     if ($file_list_len && $file_list_len > 1) {      if ($file_list_len && $file_list_len > 1) {
       while (scalar(@files) > $file_list_len) {  # Process files in chunks.        while (scalar(@files) > $file_list_len) {  # Process files in chunks.
         system(@cmd, splice(@files, 0, $file_list_len)) == 0 or exit -1;          system(@cmd, splice(@files, 0, $file_list_len)) == 0 or exit -1;
Line 2579  sub getDirLogs($$@)
Line 2369  sub getDirLogs($$@)
   
   if ($linesread == 0) {    if ($linesread == 0) {
     fatal('500 Internal Error',      fatal('500 Internal Error',
           'Failed to spawn GNU rlog on <em>"%s"</em>.<br /><br />Did you set the <b><code>@command_path</code></b> in your configuration file correctly? (Currently: "<code>%s</code>")',            'Failed to spawn rlog on <em>"%s"</em>',
           htmlquote(join(', ', @files)), join(':', @command_path));            htmlquote(join(', ', @files)));
   }    }
   
   return @unreadable;    return @unreadable;
 }  }
   
Line 2604  sub readLog($;$)
Line 2393  sub readLog($;$)
   
   my $fh = do { local (*FH); };    my $fh = do { local (*FH); };
   if (!open($fh, "-|")) {    # child    if (!open($fh, "-|")) {    # child
     openOutputFilter();  
     $revision = defined($revision) ? "-r$revision" : '';      $revision = defined($revision) ? "-r$revision" : '';
     if ($revision =~ /\./) {      if ($revision =~ /\./) {
       # Normal revision, not a branch/tag name.        # Normal revision, not a branch/tag name.
Line 2830  sub getDiffLinks($$$)
Line 2618  sub getDiffLinks($$$)
            &link(htmlquote(lc($DIFFTYPES{$difftype}{descr})), "$url;f=$f"));             &link(htmlquote(lc($DIFFTYPES{$difftype}{descr})), "$url;f=$f"));
     }      }
   }    }
   if (my $extdiffs = $DIFF_COMMANDS{lc($mimetype)}) {  
     for my $i (0 .. scalar(@$extdiffs)-1) {  
       my $extdiff = $extdiffs->[$i];  
       push(@links, &link(htmlquote($extdiff->{name}), "$url;f=ext$i"))  
         if ($extdiff->{cmd} && $extdiff->{name});  
     }  
   }  
   return @links;    return @links;
 }  }
   
Line 2888  sub printLog($$$;$$)
Line 2669  sub printLog($$$;$$)
     print ' - view: ', join(', ', @vlinks) if @vlinks;      print ' - view: ', join(', ', @vlinks) if @vlinks;
     undef @vlinks;      undef @vlinks;
   
     if (!$isbin && $allow_version_select) {      unless ($isbin) {
       print ' - ';        print ' - ';
       if ($isSelected) {        if ($isSelected) {
         print '<b>[selected&nbsp;for&nbsp;diffs]</b>';          print '<b>[selected&nbsp;for&nbsp;diffs]</b>';
Line 2902  sub printLog($$$;$$)
Line 2683  sub printLog($$$;$$)
   print "<br />\n";    print "<br />\n";
   
   print '<i>';    print '<i>';
   if (@mytz) {    print scalar gmtime($date{$_}), ' UTC</i> (';
     my ($est) = $mytz[(localtime($date{$_}))[8]];  
     print scalar localtime($date{$_}), " $est</i> (";  
   } else {  
     print scalar gmtime($date{$_}), " UTC</i> (";  
   }  
   print readableTime(time() - $date{$_}, 1), ' ago)';    print readableTime(time() - $date{$_}, 1), ' ago)';
   print ' by <i>', htmlquote($author{$_}), "</i><br />\n";    print ' by <i>', htmlquote($author{$_}), "</i><br />\n";
   
Line 3054  sub doLog($)
Line 2830  sub doLog($)
     &clickablePath($upwhere, 1), "</b>\n</p>\n";      &clickablePath($upwhere, 1), "</b>\n</p>\n";
   print "<p>\n ";    print "<p>\n ";
   print &link('Request diff between arbitrary revisions', '#diff');    print &link('Request diff between arbitrary revisions', '#diff');
   if ($cvshistory_url) {  
     (my $d = $upwhere) =~ s|/+$||;  
     print ' - ', history_link($d, $filename);  
   }  
   print "\n</p>\n<hr />\n";    print "\n</p>\n<hr />\n";
   
   print "<p>\n";    print "<p>\n";
Line 3121  EOF
Line 2893  EOF
   my $diffrev = defined($input{r1}) ?    my $diffrev = defined($input{r1}) ?
     $input{r1} : $revdisplayorder[$#revdisplayorder];      $input{r1} : $revdisplayorder[$#revdisplayorder];
   
   printf(<<EOF, $inputTextSize, $diffrev);    printf(<<EOF, $diffrev);
 <input type="text" size="%s" name="tr1" value="%s" onchange="this.form.r1.selectedIndex=0" />  <input type="text" size="12" name="tr1" value="%s" onchange="this.form.r1.selectedIndex=0" />
 </td>  </td>
 <td></td>  <td></td>
 </tr>  </tr>
Line 3138  EOF
Line 2910  EOF
   
   $diffrev = defined($input{r2}) ? $input{r2} : $revdisplayorder[0];    $diffrev = defined($input{r2}) ? $input{r2} : $revdisplayorder[0];
   
   printf(<<EOF, $inputTextSize, $diffrev, $scriptwhere);    printf(<<EOF, $diffrev, $scriptwhere);
 <input type="text" size="%s" name="tr2" value="%s" onchange="this.form.r2.selectedIndex=0" />  <input type="text" size="12" name="tr2" value="%s" onchange="this.form.r2.selectedIndex=0" />
 </td>  </td>
 <td><input type="submit" value="Get Diffs" accesskey="G" /></td>  <td><input type="submit" value="Get Diffs" accesskey="G" /></td>
 </tr>  </tr>
Line 3453  EOF
Line 3225  EOF
 }  }
   
   
 sub doEnscript($$$;$)  
 {  
   my ($filehandle, $highlight, $linenumbers, $lang) = @_;  
   $lang ||= 'cvsweb';  
   
   my @cmd = ($CMD{enscript},  
              @enscript_options,  
              '-q', "--language=$lang", '-o', '-', "--highlight=$highlight");  
   
   local *ENSCRIPT_OUT;  
   my ($h, $err) =  
     startproc(\@cmd, $filehandle, '>pipe', \*ENSCRIPT_OUT);  
   fatal('500 Internal Error', $err) unless $h;  
   
   # We could short-circuit and have enscript output directly to STDOUT above,  
   # but that doesn't work with mod_perl (at least some 1.99 versions).  
   if ($linenumbers) {  
     my $ln = 0;  
     while (<ENSCRIPT_OUT>) {  
       printf '<a id="l%d" class="src">%5d: </a>', (++$ln) x 2;  
       print $_;  
     }  
   } else {  
     local $/ = undef;  
     print <ENSCRIPT_OUT>;  
   }  
   $h->finish();  
 }  
   
   
 #  #
 # The passed in $path and $filename should not be URI escaped, and $swhere  # The passed in $path and $filename should not be URI escaped, and $swhere
 # *should* be.  # *should* be.
Line 3673  EOF
Line 3415  EOF
 }  }
   
   
 sub chooseMirror()  
 {  
   # This code comes from the original BSD-cvsweb  
   # and may not be useful for your site; If you don't  
   # set %MIRRORS this won't show up, anyway.  
   scalar(%MIRRORS) or return;  
   
   # Should perhaps exclude the current site somehow...  
   print "\n<p>\nThis CVSweb is mirrored in\n";  
   
   my @tmp = map(&link(htmlquote($_), $MIRRORS{$_}), sort keys %MIRRORS);  
   my $tmp = pop (@tmp);  
   
   if (scalar(@tmp)) {  
     print join (', ', @tmp), ' and ';  
   }  
   
   print "$tmp.\n</p>\n";  
 }  
   
   
 sub fileSortCmp()  sub fileSortCmp()
 {  {
   (my $af = $a) =~ s/,v$//;    (my $af = $a) =~ s/,v$//;
Line 3820  sub display_link($$;$$)
Line 3541  sub display_link($$;$$)
                  htmlquote($textlink));                   htmlquote($textlink));
 }  }
   
 #  
 # Returns a link to CVSHistory for the given directory and filename.  
 #  
 sub history_link($$;$)  
 {  
   my ($dir, $file, $text) = @_;  
   $dir  ||= '';  
   $file ||= '';  
   $text ||= 'History';  
   return &link($text,  
                sprintf('%s?cvsroot=%s;dsearch=%s;fsearch=%s;limit=1',  
                        $cvshistory_url, uri_escape($input{cvsroot} || ''),  
                        uri_escape($dir), uri_escape($file)));  
 }  
   
 # Returns a Query string with the  # Returns a Query string with the
 # specified parameter toggled  # specified parameter toggled
 sub toggleQuery($;$)  sub toggleQuery($;$)
Line 3899  sub http_header(;$$)
Line 3605  sub http_header(;$$)
 {  {
   my ($content_type, $moddate) = @_;    my ($content_type, $moddate) = @_;
   $content_type ||= 'text/html';    $content_type ||= 'text/html';
     $content_type .= '; charset="UTF-8"' if $content_type =~ /^text\//;
   
   $content_type .= "; charset=$charset"  
     if ($charset && $content_type =~ m,^text/,);  
   
   # Note that in the following, we explicitly join() and concatenate the    # Note that in the following, we explicitly join() and concatenate the
   # headers instead of printing them as an array.  This is because some    # headers instead of printing them as an array.  This is because some
   # systems, eg. early versions of mod_perl 2 don't quite get it if the    # systems, eg. early versions of mod_perl 2 don't quite get it if the
Line 4028  sub runproc(@)
Line 3732  sub runproc(@)
     $errormsg = "'@{$_[0]}' failed: $@";      $errormsg = "'@{$_[0]}' failed: $@";
   }    }
   return ($exitcode, $errormsg);    return ($exitcode, $errormsg);
 }  
   
 #  
 # Check out a file to a temporary file.  
 #  
 sub checkout_to_temp($$$)  
 {  
   my ($cvsroot, $cvsname, $rev) = @_;  
   
   # Pipe given cvs file into a temporary place.  
   my ($temp_fh, $temp_fn) = tempfile('.cvsweb.XXXXXXXX', DIR => tmpdir());  
   
   my @cmd = ($CMD{cvs}, @cvs_options, '-Qd', $cvsroot,  
              'co', '-p', "-r$rev", $cvsname);  
   
   local (*DIFF_OUT);  
   my ($h, $err) = startproc(\@cmd, \"", '>pipe', \*DIFF_OUT);  
   if ($h) {  
     local $/ = undef;  
     print $temp_fh <DIFF_OUT>;  
     $h->finish();  
     close($temp_fh);  
   } else {  
     close($temp_fh);  
     unlink($temp_fn);  
     fatal('500 Internal Error',  
           'Checkout failure (exit status %s), output: <pre>%s</pre>',  
           $? >> 8 || -1, $err);  
   }  
   
   return $temp_fn;  
 }  }
   
 #  #

Legend:
Removed from v.4.18  
changed lines
  Added in v.4.39

CVSweb