#! /usr/bin/perl -w # # Copyright (C) 2001 Richard Kettlewell # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA #/ use strict; use integer; use IO::File; use IO::Handle; # configurable items: # root of SCCS files my $root = "/cdrom"; # allowed filters my @allowed_filters = qw(raw diff); # add "man2html" if you trust groff my $tmproot; my $tmpcount = 0; my %allowed_filters = map(($_ => 1), @allowed_filters); sub decode( $ ); sub quote( $ ); sub directory( $ ); sub regfile( $ ); sub dirent( $$ ); sub tempfile(); sub filter( $$$ ); sub save( $ ); sub copy( $$ ); my @query = split(/\&/, $ENV{'QUERY_STRING'}); my %q = (); for(@query) { if(/^([^=]+)=(.*)$/) { $q{decode($1)} = decode($2); } } my $path = $ENV{'PATH_INFO'} || ""; error("invalid path \"$path\"") if $path =~ /\.\./; if(-d "$root/$path" && $path !~ /\/$/) { # make sure directory URLs always end in / my $url = "$ENV{'REQUEST_URI'}/"; # REQUEST_URI might or might not include http://.... if($url !~ /^http:/) { if($ENV{'SERVER_PORT'} eq "80") { $url = "http://$ENV{'SERVER_NAME'}$url"; } else { $url = "http://$ENV{'SERVER_NAME'}:$ENV{'SERVER_PORT'}$url"; } } output("Location: $url\n"); html("Redirect", "

redirect to ", quote($url), "

\n"); } else { stat "$root/$path"; if (-d _) { directory($path); } elsif (-f _) { regfile($path); } else { error("\"$path\" is not a valid path"); } } (close STDOUT) || die "$0: stdout: $!"; exit 0; sub directory( $ ) { opendir(D, "$root/$path") || error("opening $path: $!"); my @files = readdir D; closedir D; my @bgcolors = ("#ffffff", "#c0ffc0"); my $n = 0; html("SCCS: $path", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", ($path ne "/" ? (" \n", dirent($path, ".."), " \n", ) : ()), map(( " \n", dirent($path, $_), " \n", ), grep { $_ ne "." && $_ ne ".." } sort @files), "
FilenameTypeUserRevDateComment
\n"); } sub dirent( $$ ) { my $path = shift; my $file = shift; my $link; if($file eq "..") { # parent directory is done specially $link = "$ENV{'SCRIPT_NAME'}$path"; $link =~ s/[^\/]+\/$//; } else { # include the trailing / on directories, to save a redirect stat "$root/$path/$file"; $link = -d _ ? "$file/" : $file; } lstat "$root/$path/$file"; my $type = (-l _ ? "link" : -d _ ? "dir" : -f _ ? "file" : "?"); my $who = " "; my $id = " "; my $date = " "; my $comment = " "; if($type eq "file") { my $prs = `sccs prs -d\Q:P: :I: :D: :C:\E \Q$root/$path/$file\E`; ($who, $id, $date, $comment) = map(quote($_), ($prs =~ /^(\S+) (\S+) (\S+) (.*)/)) if !$?; } return (" \n", " ", quote($file), "\n", " \n", " $type\n", " ", $who, "\n", " ", $id, "\n", " ", $date, "\n", " ", $comment, "\n", ); } sub regfile( $ ) { my $path = shift; local $_; (my $file = $path) =~ s/^.*\///; if(exists $q{'r'}) { # output file contents (my $sccs = new IO::File("sccs get -p -r\Q$q{'r'}\E \Q$root/$path\E|")) || die "$0: executing sccs command: $!"; filter($q{'f'} || "raw", $sccs, *STDOUT); $sccs->close() || die "$0: sccs error: $?/$!"; } elsif(exists $q{'d'}) { # output a diff (my $sccs = new IO::File("sccs sccsdiff -r\Q$q{'d'}\E -r\Q$q{'e'}\E -u \Q$root/$path\E|")) || die "$0: executing sccs command: $!"; filter($q{'f'} || "raw", $sccs, *STDOUT); $sccs->close() || die "$0: sccs error: $?/$!"; } else { # prs output my @prs = `sccs prs \Q$root/$path\E`; if($?) { html("SCCS: $path", "

Cannot get any SCCS information for ", quote($path), "

\n"); return; } my @revs = (); my %when = (); my %who = (); my %comments = (); my $rev; my $incomment = 0; for(@prs) { if(/^D ([0-9\.]+) (\d+\/\d+\/\d+ \d+:\d+:\d+) (\S+)/) { $rev = $1; push(@revs, $rev); $when{$rev} = quote($2); $who{$rev} = quote($3); $incomment = 0; } elsif(/^COMMENTS:/) { $incomment = 1; } elsif($incomment) { if(exists $comments{$rev}) { $comments{$rev} .= "
" . quote($_); } else { $comments{$rev} = quote($_); } } } my @bgcolors = ("#ffffff", "#c0ffc0"); my $n = 0; html("SCCS: $path", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", map(( " \n", " \n", " \n", " \n", " \n", " \n", " \n", ), 0 .. $#revs), "
Rev UserWhenComments
", "$revs[$_]", "", $_ != $#revs ? (" (diff)", ) : " ", $path =~ /\.\d+$/ && exists $allowed_filters{'man2html'} ? (" (format)", ) : (), "$who{$revs[$_]}$when{$revs[$_]}$comments{$revs[$_]}
\n", "
\n", "

Revisions: ", " \n", " \n", " \n", "

\n", "
\n", ); } } sub decode( $ ) { local $_ = shift; s/\+/ /g; s/%(..)/chr(hex($1))/ge; return $_; } sub error { html("Error", "

", map(quote($_), @_), "

\n"); } sub html { my $title = quote(shift); output("Content-Type: text/html\n", "\n", "\n", "\n", " \n", " $title\n", " \n", " \n", "

$title

\n", # map(("

", quote($_), "=", quote($ENV{$_}), "

\n"), # sort keys %ENV), @_, " \n", "\n", "\n", "\n", ); } sub quote( $ ) { local $_ = shift; s/[\&<>\"]/sprintf("&#%d;", ord($&))/ge; return $_; } sub output { (print @_) || die "$0: stdout: $!"; } sub tempfile() { ++$tmpcount; if(!defined $tmproot) { my $tmpdir = $ENV{'TMPDIR'} || "/tmp"; for(my $n = 0; $n < 32; ++$n) { my $t = "$tmpdir/sccs.$$.$n"; if(mkdir($t, 0700)) { $tmproot = $t; return "$tmproot/$tmpcount"; } } die "$0: cannot create a temporary directory"; } return "$tmproot/$tmpcount"; } # filter(TYPE, IN, OUT) # # Read data from IN, apply filter TYPE, write to OUT sub filter( $$$ ) { my ($type, $in, $out) = @_; $type = "\L$type\E"; if(!exists $allowed_filters{$type}) { $type = "raw"; } if($type eq "man2html") { my $path = save($in); my $command = `grog -Thtml \Q$path\E`; die "$0: grog failed" if $?; output("Content-Type: text/html\n", "\n", ); command($command, $out); return; } if($type eq "diff") { local $_; output("Content-Type: text/html\n", "\n", "\n", " \n", "
",
	  );
    while(defined($_ = $in->getline())) {
      my $color;
      chomp;
      if(/^\+/) {
	$color = "#0000ff";
      } elsif(/^-/) {
	$color = "#ff0000";
      } elsif(/^[^ ]/) {
	$color = "#00ff00";
      }
      if(defined $color) {
	output("", quote($_), "\n");
      } else {
	output(quote($_), "\n");
      }
    }
    return;
  }
  # raw
  output("Content-Type: text/plain\n",
	 "\n");
  copy($in, $out);
}

# save(HANDLE)
#
# read from HANDLE and save to a temporary file.  Return name of temporary
# file.

sub save( $ ) {
  my $in = shift;
  my $tmpfile = tempfile;
  (my $o = new IO::File($tmpfile, "w"))
    || die "$0: opening $tmpfile: $!";
  copy($in, $o);
  $o->close()
    || die "$0: writing $tmpfile: $!";
  return $tmpfile;
}

# command(COMMAND, OUT)
#
# Execute COMMAND and send the output to OUT

sub command( $$ ) {
  my ($command, $out) = @_;
  (my $i = new IO::File("$command|"))
    || die "$0: executing $command: $!";
  copy($i, $out);
  $i->close()
    || die "$0: read error: $!/$?";
}

# copy(IN, OUT)
#
# copy data from IN to OUT

sub copy( $$ ) {
  my ($in, $out) = @_;
  my $b;
  my $n;
  while(($n = $in->read($b, 1024))) {
    $out->print($b)
      || die "$0: write error: $!";
  }
  die "$0: read error: $!" if ! defined $n;
}

END {
  if(defined $tmproot) {
    system("rm -rf $tmproot");
  }
}