#!/usr/bin/perl eval "exec perl -S $0 $*" if $running_under_some_shell; # $Id: patdiff.SH 1 2006-08-24 12:32:52Z rmanfredi $ # # Copyright (c) 1991-1997, 2004-2006, Raphael Manfredi # # You may redistribute only under the terms of the Artistic Licence, # as specified in the README file that comes with the distribution. # You may reuse parts of this distribution only within the terms of # that same Artistic Licence; a copy of which may be found at the root # of the source tree for dist 4.0. # # Original Author: Larry Wall # # $Log: patdiff.SH,v $ # Revision 3.0.1.2 1994/01/24 14:30:36 ram # patch16: now prefix error messages with program's name # patch16: added ~/.dist_profile awareness # # Revision 3.0.1.1 1993/08/19 06:42:35 ram # patch1: leading config.sh searching was not aborting properly # # Revision 3.0 1993/08/18 12:10:43 ram # Baseline for dist 3.0 netwide release. # $version = '3.5'; $patchlevel = '0'; $RCSEXT = ',v' unless $RCSEXT; $TOPDIR = ''; # We are at top-level directory $progname = &profile; # Read ~/.dist_profile require 'getopts.pl'; &usage unless $#ARGV >= 0; &usage unless &Getopts("ahnV"); if ($opt_V) { print STDERR "$progname $version PL$patchlevel\n"; exit 0; } elsif ($opt_h) { &usage; } &readpackage; ©right'init($copyright) if -f $copyright; system 'mkdir', 'bugs' unless -d 'bugs'; if (-f 'patchlevel.h') { open(PL,"patchlevel.h") || die "$progname: can't open patchlevel.h: $!\n"; while () { $bnum = $1 if /^#define\s+PATCHLEVEL\s+(\d+)/; } die "$progname: malformed patchlevel.h file.\n" if $bnum eq ''; ++$bnum; } else { $bnum=1; } if ($opt_a) { open(MANI,"MANIFEST.new") || die "$progname: can't read MANIFEST.new: $!\n"; @ARGV = (); while () { chop; ($_) = split(' '); next if -d; push(@ARGV,$_); } close MANI; } foreach $file (@ARGV) { next if ($file =~ /^patchlevel.h$/); # Skip patchlevel.h if (! -f $file) { print "$progname: $file not found.\n"; next; } $files = &rcsargs($file); @files = split(' ',$files); $new=''; $revs=0; $rlog = `rlog -rlastpat- $files 2>&1`; ($lastpat) = ($rlog =~ /lastpat: ([\d.]+)/); ($revs) = ($rlog =~ /selected revisions: (\d+)/); if (!$revs) { print "$progname: no cil has been done on $file.\n" ;; } elsif ($revs == 1) { ($base) = ($rlog =~ /.*\nrevision\s+(\S+)/); ($a,$b,$c,$d) = split(/\./,$base); if ($d ne '') { if (!$opt_n) { print "$progname: no changes in $file since last patch. (Did you cil it?)\n"; next; # Skip file with no changes } else { $new='foo'; } } else { $revs=0; $rlog = `rlog -r$revbranch- $files 2>&1`; ($revs) = ($rlog =~ /selected revisions: (\d+)/); if (!$revs) { print "$progname: no changes in $file since base version. (Did you cil it?)\n"; next; # Skip file with no changes } else { ($new) = ($rlog =~ /\nrevision\s*(\d+\.\d+\.\d+\.\d+)/); } } } else { ($new) = ($rlog =~ /\nrevision\s*(\d+\.\d+\.\d+\.\d+)/); } if ($new ne '') { ($fname = $file) =~ s|.*/||; $fname = substr($fname, 0, 11); # For filsystems with short names open(PATCH,">>bugs/$fname.$bnum") || die "Can't make patch"; print PATCH "\nIndex: $file\n"; open(CO,"co -p -rlastpat $files 2>/dev/null |"); while () { if (/\$Header/ || /\$Id/) { print PATCH "Prereq: $lastpat\n"; last; } } close CO; if (!$opt_n) { if ($mydiff eq '') { open(DIFF,"rcsdiff -c -rlastpat -r$new $files |") || die "$progname: can't fork rcsdiff: $!\n"; while () { if ($. == 1) {s|\*\*\* \S+ |*** $file.old |;} if ($. == 2) {s|--- \S+ |--- $file |;} s|Lock[e]r:.*\$|\$|; # Use [e] to make it safe on itself print PATCH; } close DIFF; system 'rcs', "-Nlastpat:$new", @files; } else { ©right'expand("co -p -rlastpat $file", "/tmp/pdo$$"); ©right'expand("co -p -r$new $file", "/tmp/pdn$$"); open(DIFF, "$mydiff /tmp/pdo$$ /tmp/pdn$$ |") || die "Can't run $mydiff"; while () { # Contextual or unified diff if ($. == 1) { s|\*\*\* \S+ |*** $file.old | || s|--- \S+ |--- $file.old |; } if ($. == 2) { s|--- \S+ |--- $file | || s|\+\+\+ \S+ |+++ $file |; } s|Lock[e]r:.*\$|\$|; # Remove locker mark print PATCH; } close DIFF; system 'rcs', "-Nlastpat:$new", @files; unlink "/tmp/pdn$$", "/tmp/pdo$$"; } } else { if ($mydiff eq '') { open(DIFF,"rcsdiff -c -rlastpat $files |") || die "Can't run rcsdiff"; while () { if ($. == 1) {s|\*\*\* \S+ |*** $file.old |;} if ($. == 2) {s|--- \S+ |--- $file |;} s|Lock[e]r:.*\$|\$|; # Remove locker mark print PATCH; } close DIFF; } else { system "co -p -rlastpat $files >/tmp/pdo$$"; system "cp $file /tmp/pdn$$"; open(DIFF, "$mydiff /tmp/pdo$$ /tmp/pdn$$ |") || die "$progname: can't fork $mydiff: $!\n"; while () { # Contextual or unified diff if ($. == 1) { s|\*\*\* \S+ |*** $file.old |; s|--- \S+ |--- $file.old |; } if ($. == 2) { s|--- \S+ |--- $file |; s|\+\+\+ \S+ |+++ $file |; } s|Lock[e]r:.*\$|\$|; # Remove locker mark print PATCH; } close DIFF; unlink "/tmp/pdn$$", "/tmp/pdo$$"; } } } } sub usage { print STDERR <) { next if /^:/; next if /^#/; if (($var,$val) = /^\s*(\w+)=(.*)/) { $val = "\"$val\"" unless $val =~ /^['"]/; eval "\$$var = $val;"; } } close PACKAGE; } sub rcsargs { local($result) = ''; local($_); while ($_ = shift(@_)) { if ($_ =~ /^-/) { $result .= $_ . ' '; } elsif ($#_ >= 0 && do equiv($_,$_[0])) { $result .= $_ . ' ' . $_[0] . ' '; shift(@_); } else { $result .= $_ . ' ' . do other($_) . ' '; } } $result; } sub equiv { local($s1, $s2) = @_; $s1 =~ s|.*/||; $s2 =~ s|.*/||; if ($s1 eq $s2) { 0; } elsif ($s1 =~ s/$RCSEXT$// || $s2 =~ s/$RCSEXT$//) { $s1 eq $s2; } else { 0; } } sub other { local($s1) = @_; ($dir,$file) = ('./',$s1) unless local($dir,$file) = ($s1 =~ m|(.*/)(.*)|); $dir = $TOPDIR . $dir if -d $TOPDIR . "$dir/RCS"; local($wasrcs) = ($file =~ s/$RCSEXT$//); if ($wasrcs) { `mkdir $dir` unless -d $dir; $dir =~ s|RCS/||; } else { $dir .= 'RCS/'; `mkdir $dir` unless -d $dir; $file .= $RCSEXT; } "$dir$file"; } package copyright; # Read in copyright file sub init { local($file) = @_; # Copyright file undef @copyright; open(COPYRIGHT, $file) || die "Can't open $file: $!\n"; chop(@copyright = ); close COPYRIGHT; } # Reset the automaton for a new file. sub reset { $copyright_seen = @copyright ? 0 : 1; $marker_seen = 0; } # Filter file, line by line, and expand the copyright string. The @COPYRIGHT@ # symbol may be preceded by some random comment. A leader can be defined and # will be pre-pended to all the input lines. sub filter { local($line, $leader) = @_; # Leader is optional return $leader . $line if $copyright_seen || $marker_seen; $marker_seen = 1 if $line =~ /\$Log[:\$]/; $copyright_seen = 1 if $line =~ /\@COPYRIGHT\@/; return $leader . $line unless $copyright_seen; local($comment, $trailer) = $line =~ /^(.*)\@COPYRIGHT\@\s*(.*)/; $comment = $leader . $comment; $comment . join("\n$comment", @copyright) . "\n"; } # Filter output of $cmd redirected into $file by expanding copyright, if any. sub expand { local($cmd, $file) = @_; if (@copyright) { open(CMD,"$cmd|") || die "Can't start '$cmd': $!\n"; open(OUT, ">$file") || die "Can't create $file: $!\n"; &reset; local($_); while () { print OUT &filter($_); } close OUT; close CMD; } else { system "$cmd > $file"; die "Command '$cmd' failed!" if $?; } } package main; # Perform ~name expansion ala ksh... # (banish csh from your vocabulary ;-) sub tilda_expand { local($path) = @_; return $path unless $path =~ /^~/; $path =~ s:^~([^/]+):(getpwnam($1))[$[+7]:e; # ~name $path =~ s:^~:$ENV{'HOME'} || (getpwuid($<))[$[+7]:e; # ~ $path; } # Set up profile components into %Profile, add any profile-supplied options # into @ARGV and return the command invocation name. sub profile { local($profile) = &tilda_expand($ENV{'DIST'} || '~/.dist_profile'); local($me) = $0; # Command name $me =~ s|.*/(.*)|$1|; # Keep only base name return $me unless -s $profile; local(*PROFILE); # Local file descriptor local($options) = ''; # Options we get back from profile unless (open(PROFILE, $profile)) { warn "$me: cannot open $profile: $!\n"; return; } local($_); local($component); while () { next if /^\s*#/; # Skip comments next unless /^$me/o; if (s/^$me://o) { # progname: options chop; $options .= $_; # Merge options if more than one line } elsif (s/^$me-([^:]+)://o) { # progname-component: value $component = $1; chop; s/^\s+//; # Trim leading and trailing spaces s/\s+$//; $Profile{$component} = $_; } } close PROFILE; return unless $options; require 'shellwords.pl'; local(@opts); eval '@opts = &shellwords($options)'; # Protect against mismatched quotes unshift(@ARGV, @opts); return $me; # Return our invocation name }