Index: ossp-pkg/shiela/ChangeLog RCS File: /v/ossp/cvs/ossp-pkg/shiela/ChangeLog,v rcsdiff -q -kk '-r1.18' '-r1.19' -u '/v/ossp/cvs/ossp-pkg/shiela/ChangeLog,v' 2>/dev/null --- ChangeLog 2002/12/21 11:29:22 1.18 +++ ChangeLog 2002/12/22 11:06:50 1.19 @@ -11,6 +11,18 @@ Changes between 0.9.2 and 0.9.3 (19-Aug-2002 to 21-Dec-2002): + *) Fully reimplemented the "Content" "details" (the change summary in + reports). It now supports both textual (via diff(1)) and binary + (via xdelta(1)) file changes. Additionally each change report part + is now a small executable shell-script which can passed through + /bin/sh for reproducing the patch locally. This way, especially + binary change reports are more meaningsful and consistent with the + textual change reports. + INCOMPATIBILITY: + "Details diff:plain" -> "Details patch:plain" + "Details diff:mime" -> "Details patch:mime" + [Ralf S. Engelschall] + *) Make sure that CVS diff handles are calculated correctly if files are added or deleted. [Ralf S. Engelschall] @@ -22,6 +34,9 @@ *) Finally really use the "Environment" configuration section to find "cvs" and "sendmail" directly and add a "Program" sub-command to it for easier extension of the "Environment" section in the future. + INCOMPATIBILITY: + "CVS " -> "Program cvs " + "Sendmail " -> "Program sendmail " [Ralf S. Engelschall] *) Consistently use IO objects instead of the anchient direct Index: ossp-pkg/shiela/shiela-install.sh RCS File: /v/ossp/cvs/ossp-pkg/shiela/shiela-install.sh,v rcsdiff -q -kk '-r1.9' '-r1.10' -u '/v/ossp/cvs/ossp-pkg/shiela/shiela-install.sh,v' 2>/dev/null --- shiela-install.sh 2002/12/21 11:00:17 1.9 +++ shiela-install.sh 2002/12/22 11:06:50 1.10 @@ -321,12 +321,24 @@ fi query U tool_diff +if [ ".$V_tool_xdelta" = . ]; then + V_tool_xdelta="xdelta" + for path in $paths; do + path=`echo $path | sed -e 's;/*$;;'` + if [ $minusx "$path/xdelta" ] && [ ! -d "$path/xdelta" ]; then + V_tool_xdelta="$path/xdelta" + break + fi + done +fi +query U tool_xdelta + if [ ".$V_tool_uuencode" = . ]; then V_tool_uuencode="uuencode" for path in $paths; do path=`echo $path | sed -e 's;/*$;;'` if [ $minusx "$path/uuencode" ] && [ ! -d "$path/uuencode" ]; then - V_tool_diff="$path/uuencode" + V_tool_uuencode="$path/uuencode" break fi done Index: ossp-pkg/shiela/shiela.pl RCS File: /v/ossp/cvs/ossp-pkg/shiela/shiela.pl,v rcsdiff -q -kk '-r1.27' '-r1.28' -u '/v/ossp/cvs/ossp-pkg/shiela/shiela.pl,v' 2>/dev/null --- shiela.pl 2002/12/21 17:02:59 1.27 +++ shiela.pl 2002/12/22 11:06:50 1.28 @@ -183,11 +183,14 @@ &find_program("cvs") || die "unable to find `cvs' program"; $RT->{diff} = $CF->{Environment}->{Program}->{diff} || - &find_program("diff") || - die "unable to find `diff' program"; + &find_program("diff") || + ''; + $RT->{xdelta} = $CF->{Environment}->{Program}->{xdelta} || + &find_program("xdelta") || + ''; $RT->{uuencode} = $CF->{Environment}->{Program}->{uuencode} || &find_program("uuencode") || - die "unable to find `uuencode' program"; + ''; # pre-calculate a reasonable MIME boundary tag my $randtag; @@ -1630,98 +1633,162 @@ } } - # determine change diff + # determine change difference summary if ($Io eq 'A') { - # file was added, so we show the whole contents + ## + ## ADDED FILE + ## + + # retrieve whole file contents + unlink("$RT->{tmpfile}.all"); + my $io = new IO::File ">$RT->{tmpfile}.all" + || die "unable to open temporary file $RT->{tmpfile}.all for writing"; + if (not $RT->{useserver}) { + my $cvs = new IO::File "$RT->{cvs} -f -l -Q -n update -p -r$Iv '$Is'|" + || die "unable to open CVS command pipe for reading"; + $io->print($_) while (<$cvs>); + $cvs->close; + } + else { + my $cvs = new CVS ($RT->{cvs}, $RT->{cvsroot}); + $cvs->directory($cvsdir); + $cvs->entry($Is); + $cvs->arguments("-p", "-r$Iv", $Is); + $cvs->send("update"); + $io->print(scalar $cvs->result); + $cvs->close; + } + $io->close; + if ($Ik eq 'b' or -B $Is) { - # file seems to be a binary file - $cvsdiff .= - "\n" . - "Index: $cvsdir/$Is\n" . - "============================================================\n" . - "\$ cvs update -p -r$Iv $Is | uuencode $Is\n"; - if (not $RT->{useserver}) { - my $io = new IO::File "$RT->{cvs} -f -l -Q -n update -p -r$Iv '$Is' | uuencode '$Is' |" - || die "unable to open CVS command pipe for reading"; + # generate binary change patch script + if ($RT->{xdelta} and $RT->{uuencode}) { + $cvsdiff .= + "\n" . + "(cd $cvsdir && uudecode <<'@@ .' && \\\n" . + " xdelta patch $Is.xdelta /dev/null $Is && \\\n" . + " rm -f $Is.xdelta)\n" . + "Index: $cvsdir/$Is\n" . + ("=" x 76) . "\n"; + unlink("$RT->{tmpfile}.null"); + unlink("$RT->{tmpfile}.xdelta"); + my $io = new IO::File ">$RT->{tmpfile}.null" + || die "unable to open temporary file $RT->{tmpfile}.null for writing"; + $io->close; + system("$RT->{xdelta} delta $RT->{tmpfile}.null " . + "$RT->{tmpfile}.all $RT->{tmpfile}.xdelta >/dev/null 2>&1"); + $io = new IO::File "uuencode $RT->{tmpfile}.xdelta $Is.xdelta |" + || die "unable to open uuencode command pipe for reading"; $cvsdiff .= $_ while (<$io>); $io->close; + $cvsdiff .= "@@ .\n"; + $cvsdiff .= "\n"; + unlink("$RT->{tmpfile}.null"); + unlink("$RT->{tmpfile}.xdelta"); } - else { - my $cvs = new CVS ($RT->{cvs}, $RT->{cvsroot}); - $cvs->directory($cvsdir); - $cvs->entry($Is); - $cvs->arguments("-p", "-r$Iv", $Is); - $cvs->send("update"); - $cvsdiff .= scalar $cvs->result; - $cvs->close; - } - $cvsdiff .= "\n"; } else { - # file seems to be a regular text file - $cvsdiff .= - "\n" . - "Index: $cvsdir/$Is\n" . - "============================================================\n" . - "\$ cvs update -p -r$Iv $Is\n"; - if (not $RT->{useserver}) { - my $io = new IO::File "$RT->{cvs} -f -l -Q -n update -p -r$Iv '$Is'|" + # generate textual change patch script + if ($RT->{diff}) { + $cvsdiff .= + "\n" . + "patch -p0 <<'@@ .'\n" . + "Index: $cvsdir/$Is\n" . + ("=" x 76) . "\n" . + "\$ cvs diff -u -r0 -r$Iv $Is\n"; + my $diff = ''; + my $io = new IO::File "$RT->{diff} -u /dev/null $RT->{tmpfile}.all|" || die "unable to open CVS command pipe for reading"; - $cvsdiff .= $_ while (<$io>); + $diff .= $_ while (<$io>); $io->close; + my $Is_quoted = quotemeta("$RT->{tmpfile}.all"); + $diff =~ s|^(\+\+\+\s+)$Is_quoted|$1$Is|m; + $cvsdiff .= $diff; + $cvsdiff .= "@@ .\n"; + $cvsdiff .= "\n"; } - else { - my $cvs = new CVS ($RT->{cvs}, $RT->{cvsroot}); - $cvs->directory($cvsdir); - $cvs->entry($Is); - $cvs->arguments("-p", "-r$Iv", $Is); - $cvs->send("update"); - $cvsdiff .= scalar $cvs->result; - $cvs->close; - } - $cvsdiff .= "\n"; } + + # cleanup + unlink("$RT->{tmpfile}.all"); } elsif ($Io eq 'M') { + ## + ## MODIFIED FILE + ## + if ($Ik eq 'b' or -B $Is) { - # file seems to be a binary file - $cvsdiff .= - "\n" . - "Index: $cvsdir/$Is\n" . - "============================================================\n" . - "\$ cvs update -p -r$IV $Is >$Is.old\n" . - "\$ cvs update -p -r$Iv $Is >$Is.new\n" . - "\$ diff -u $Is.old $Is.new\n"; - if (not $RT->{useserver}) { - system("$RT->{cvs} -f -l -Q -n update -p -r$IV '$Is' | $RT->{uuencode} '$Is' >$Is.old"); - system("$RT->{cvs} -f -l -Q -n update -p -r$Iv '$Is' | $RT->{uuencode} '$Is' >$Is.new"); - } - else { - my $cvs = new CVS ($RT->{cvs}, $RT->{cvsroot}); - $cvs->directory($cvsdir); - $cvs->entry($Is); - $cvs->arguments("-p", "-r$IV", $Is); - $cvs->send("update"); - my $data = scalar $cvs->result; - my $io = new IO::File ">$Is.old" || die "cannot write to $Is.old"; - $io->print($data); + # generate binary change patch script + + if ($RT->{xdelta} and $RT->{uuencode}) { + # retrieve whole file contents (old revision) + unlink("$RT->{tmpfile}.old"); + my $io = new IO::File ">$RT->{tmpfile}.old" + || die "unable to open temporary file $RT->{tmpfile}.old for writing"; + if (not $RT->{useserver}) { + my $cvs = new IO::File "$RT->{cvs} -f -l -Q -n update -p -r$IV '$Is'|" + || die "unable to open CVS command pipe for reading"; + $io->print($_) while (<$cvs>); + $cvs->close; + } + else { + my $cvs = new CVS ($RT->{cvs}, $RT->{cvsroot}); + $cvs->directory($cvsdir); + $cvs->entry($Is); + $cvs->arguments("-p", "-r$IV", $Is); + $cvs->send("update"); + $io->print(scalar $cvs->result); + $cvs->close; + } $io->close; - $cvs->arguments("-p", "-r$Iv", $Is); - $cvs->send("update"); - $data = scalar $cvs->result; - $io = new IO::File ">$Is.new" || die "cannot write to $Is.old"; - $io->print($data); + + # retrieve whole file contents (new revision) + unlink("$RT->{tmpfile}.new"); + $io = new IO::File ">$RT->{tmpfile}.new" + || die "unable to open temporary file $RT->{tmpfile}.new for writing"; + if (not $RT->{useserver}) { + my $cvs = new IO::File "$RT->{cvs} -f -l -Q -n update -p -r$Iv '$Is'|" + || die "unable to open CVS command pipe for reading"; + $io->print($_) while (<$cvs>); + $cvs->close; + } + else { + my $cvs = new CVS ($RT->{cvs}, $RT->{cvsroot}); + $cvs->directory($cvsdir); + $cvs->entry($Is); + $cvs->arguments("-p", "-r$Iv", $Is); + $cvs->send("update"); + $io->print(scalar $cvs->result); + $cvs->close; + } $io->close; - $cvs->close; + + # generate change patch script + $cvsdiff .= + "\n" . + "(cd $cvsdir && uudecode <<'@@ .' && \\\n" . + " mv $Is $Is.orig && xdelta patch $Is.xdelta $Is.orig $Is && \\\n" . + " rm -f $Is.orig $Is.xdelta)\n" . + "Index: $cvsdir/$Is\n" . + ("=" x 76) . "\n"; + unlink("$RT->{tmpfile}.xdelta"); + system("$RT->{xdelta} delta $RT->{tmpfile}.old " . + "$RT->{tmpfile}.new $RT->{tmpfile}.xdelta >/dev/null 2>&1"); + $io = new IO::File "uuencode $RT->{tmpfile}.xdelta $Is.xdelta |" + || die "unable to open uuencode command pipe for reading"; + $cvsdiff .= $_ while (<$io>); + $io->close; + $cvsdiff .= "@@ .\n"; + $cvsdiff .= "\n"; + unlink("$RT->{tmpfile}.xdelta"); + + # cleanup + unlink("$RT->{tmpfile}.old"); + unlink("$RT->{tmpfile}.new"); } - my $io = new IO::File "$RT->{diff} -u $Is.old $Is.new|" - || die "unable to open diff command pipe for reading"; - $cvsdiff .= $_ while (<$io>); - $io->close; - $cvsdiff .= "\n"; } else { - # file was modified, so we show the changed contents only + # generate textual change patch script my $d = ''; if (not $RT->{useserver}) { my $io = new IO::File "$RT->{cvs} -f -l -Q -n diff -u -r$IV -r$Iv '$Is'|" @@ -1743,23 +1810,30 @@ $d =~ s|^(---\s+)${Is_quoted}(\s+)|$1$cvsdir/$Is$2|m; $d =~ s|^(\+\+\+\s+)${Is_quoted}(\s+)|$1$cvsdir/$Is$2|m; $cvsdiff .= - "\n" . + "\n" . + "patch -p0 <<'@@ .'\n" . "Index: $cvsdir/$Is\n" . - "============================================================\n" . + ("=" x 76) . "\n" . "\$ cvs diff -u -r$IV -r$Iv $Is\n" . $d . - "\n"; + "@@ .\n" . + "\n"; } } elsif ($Io eq 'R') { - if ($Ik eq 'b' or -B $Is) { - # file seems to be a binary file - # FIXME - } - else { - # file seems to be a regular file - # FIXME - } + ## + ## REMOVED FILE + ## + + # generate binary and textaual change patch script + $cvsdiff .= + "\n" . + "(cat <<'@@ .' >/dev/null && rm -f $cvsdir/$Is)\n" . + "Index: $cvsdir/$Is\n" . + ("=" x 76) . "\n" . + "[NO CHANGE SUMMARY BECAUSE FILE AS A WHOLE IS JUST REMOVED]\n" . + "@@ .\n" . + "\n"; } $info = "$cvsdir/$Is,$IV,$Iv,$It,$Io,$Ik,$ID,$Id"; @@ -1825,7 +1899,7 @@ $e->{delta} = $Id; $e->{diff} = ''; my $Is_quoted = quotemeta($Is); - $cvsdiff =~ s|\n\n(.+?\n)|$e->{diff} = $1, ''|se; + $cvsdiff =~ s|\n\n(.+?\n)|$e->{diff} = $1, ''|se; $IN->{file}->{$Is} = $e; $handle_min = $ID if ($ID ne '' and $ID ne '0' and (not defined($handle_min) or $handle_min > $ID)); $handle_max = $ID if ($ID ne '' and $ID ne '0' and (not defined($handle_max) or $handle_max < $ID)); @@ -1897,7 +1971,7 @@ my $sm = new Sendmail ($RT, $logurl); $sm->header('Subject', $subject); if (defined($CF->{Logging}->{Report}->{$logtype}->{Details})) { - if ($CF->{Logging}->{Report}->{$logtype}->{Details} eq 'diff:mime') { + if ($CF->{Logging}->{Report}->{$logtype}->{Details} eq 'patch:mime') { $sm->header('Content-Type', "multipart/mixed; boundary=\"".$RT->{mimeboundary}."\""); } @@ -1961,7 +2035,7 @@ my $RP = $CF->{Logging}->{Report}->{$type} || die "No report of type `$type' defined"; my $prefix = $RP->{Prefix} || ''; - my $style = $RP->{Details} || 'diff:plain'; + my $style = $RP->{Details} || 'patch:plain'; my $O = ''; foreach my $content (@{$RP->{Content}}) { @@ -2099,7 +2173,7 @@ $O .= "$prefix$url\n"; } } - elsif ($style eq 'diff:plain') { + elsif ($style eq 'patch:plain') { foreach $file (sort(keys(%{$IN->{file}}))) { next if ($IN->{file}->{$file}->{op} eq 'T'); my $diff = $IN->{file}->{$file}->{diff}; @@ -2107,7 +2181,7 @@ $O .= $diff; } } - elsif ($style eq 'diff:mime') { + elsif ($style eq 'patch:mime') { foreach $file (sort(keys(%{$IN->{file}}))) { next if ($IN->{file}->{$file}->{op} eq 'T'); my $diff = $IN->{file}->{$file}->{diff}; @@ -2131,7 +2205,7 @@ $O =~ s|\n+$|\n|s; # MIME post-processing - if ($style eq 'diff:mime') { + if ($style eq 'patch:mime') { $O = "This is a multi-part message in MIME format.\n" . "--".$RT->{mimeboundary}."\n" . "Content-Type: text/plain; charset=iso-8859-1\n" . @@ -2147,4 +2221,3 @@ return $O; } -##EOF## Index: ossp-pkg/shiela/shiela.pod RCS File: /v/ossp/cvs/ossp-pkg/shiela/shiela.pod,v rcsdiff -q -kk '-r1.8' '-r1.9' -u '/v/ossp/cvs/ossp-pkg/shiela/shiela.pod,v' 2>/dev/null --- shiela.pod 2002/12/21 11:00:17 1.8 +++ shiela.pod 2002/12/22 11:06:50 1.9 @@ -91,6 +91,7 @@ [Program sendmail ;] [Program cvs ;] [Program diff ;] + [Program xdelta ;] [Program uuencode ;] }; @@ -102,29 +103,47 @@ This overrides the path to the F executable. Per default B determines this automatically through the inherited C<$PATH> -variable. Use this either if you have Sendmail not in C<$PATH> or if you -have multiple Sendmail instances installed. +variable. Use this either if you have B not in C<$PATH> or if you +have multiple B instances installed. The F executable +is mandatory for the operation of B. =item BpathE;> This overrides the path to the F executable. Per default B determines this automatically through the inherited C<$PATH> variable. -Use this either if you have CVS not in C<$PATH> or if you have multiple -CVS instances installed. +Use this either if you have B not in C<$PATH> or if you have multiple +B instances installed. The F executable is mandatory for the +operation of B. =item BpathE;> This overrides the path to the F executable. Per default B determines this automatically through the inherited C<$PATH> -variable. Use this either if you have GNU Diffutils not in C<$PATH> or -if you have multiple GNU Diffutils instances installed. +variable. Use this either if you have B not in C<$PATH> +or if you have multiple B instances installed. The +F executable is required for the operation of B +if change summary information on text files is used in reports (see +C
in C). + +=item BpathE;> + +This overrides the path to the F executable. Per default B determines this automatically through the inherited C<$PATH> +variable. Use this either if you have B not in C<$PATH> or if +you have multiple B instances installed. The F +executable is required for the operation of B if change +summary information on binary files is used in reports (see C +in C). =item BpathE;> This overrides the path to the F executable. Per default B determines this automatically through the inherited -C<$PATH> variable. Use this either if you have GNU Sharutils not in -C<$PATH> or if you have multiple GNU Sharutils instances installed. +C<$PATH> variable. Use this either if you have B not in +C<$PATH> or if you have multiple B instances installed. +The F executable is required for the operation of B if change summary information on binary files is used in reports +(see C in C). =back @@ -361,11 +380,11 @@ =item B
typeE:EargE;> This configures how the details (see B directive) actually look. The -following type/argument combinations are currently supported: "C" +following type/argument combinations are currently supported: "C" (the default) which produces a long details text consisting of concatenated -"C" outputs; "C" which produces the same contents as -"C", but instead of just concatenating the individual difference -texts, it encapsulates them into MIME attachments (which is useful when the +patching shell commands for reproducing the change; "C" which produces the same contents as +"C", but instead of just concatenating the individual scripts, +it encapsulates them into MIME attachments (which is useful when the EtargetE of a B directive is an Email address only); and "CI" which produces just an URL per modified file by expanding "C<%s>" (the file path relative to C<$CVSROOT>), "C<%V>" (the old revision) @@ -392,6 +411,7 @@ Program sendmail /e/foo/sw/bin/sendmail; Program cvs /e/foo/sw/bin/cvs; Program diff /e/foo/sw/bin/diff; + Program xdelta /e/foo/sw/bin/xdelta; Program uuencode /e/foo/sw/bin/uuencode; }; @@ -427,12 +447,12 @@ Acl modules *:core *:devel; Acl * *:core; Log passwd none; - Log modules mail.diff:foo-cvs@foo.dom file:CVSROOT/commit.log; - Log * mail.diff:foo-core@foo.dom; + Log modules mail.patch:foo-cvs@foo.dom file:CVSROOT/commit.log; + Log * mail.patch:foo-core@foo.dom; }; Module foo-adm "FOO Administration" { Acl * *:core; - Log * mail.diff:foo-core@foo.dom file:CVSROOT/commit.log; + Log * mail.patch:foo-core@foo.dom file:CVSROOT/commit.log; }; Module foo-src "FOO Source" { Acl *:VENDOR foo; @@ -447,10 +467,10 @@ # define the logging details Logging { Reports { - Report mail.diff { + Report mail.patch { Prefix " "; Content title rule header files log summary rule details; - Details diff:mime; + Details patch:mime; }; Report mail.url { Prefix " ";