This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Merge pull request #62 from Perl/fix-repeated-words
[metaconfig.git] / bin / patdiff
CommitLineData
459d3fb5
MBT
1#!/usr/bin/perl
2 eval "exec perl -S $0 $*"
3 if $running_under_some_shell;
4
5# $Id: patdiff.SH 1 2006-08-24 12:32:52Z rmanfredi $
6#
7# Copyright (c) 1991-1997, 2004-2006, Raphael Manfredi
8#
9# You may redistribute only under the terms of the Artistic Licence,
10# as specified in the README file that comes with the distribution.
11# You may reuse parts of this distribution only within the terms of
12# that same Artistic Licence; a copy of which may be found at the root
13# of the source tree for dist 4.0.
14#
15# Original Author: Larry Wall <lwall@netlabs.com>
16#
17# $Log: patdiff.SH,v $
18# Revision 3.0.1.2 1994/01/24 14:30:36 ram
19# patch16: now prefix error messages with program's name
20# patch16: added ~/.dist_profile awareness
21#
22# Revision 3.0.1.1 1993/08/19 06:42:35 ram
23# patch1: leading config.sh searching was not aborting properly
24#
25# Revision 3.0 1993/08/18 12:10:43 ram
26# Baseline for dist 3.0 netwide release.
27#
28
29$version = '3.5';
30$patchlevel = '0';
31
32$RCSEXT = ',v' unless $RCSEXT;
33$TOPDIR = ''; # We are at top-level directory
34
35$progname = &profile; # Read ~/.dist_profile
36require 'getopts.pl';
37&usage unless $#ARGV >= 0;
38&usage unless &Getopts("ahnV");
39
40if ($opt_V) {
41 print STDERR "$progname $version PL$patchlevel\n";
42 exit 0;
43} elsif ($opt_h) {
44 &usage;
45}
46
47&readpackage;
48&copyright'init($copyright) if -f $copyright;
49
50system 'mkdir', 'bugs' unless -d 'bugs';
51
52if (-f 'patchlevel.h') {
53 open(PL,"patchlevel.h") || die "$progname: can't open patchlevel.h: $!\n";
54 while (<PL>) {
55 $bnum = $1 if /^#define\s+PATCHLEVEL\s+(\d+)/;
56 }
57 die "$progname: malformed patchlevel.h file.\n" if $bnum eq '';
58 ++$bnum;
59} else {
60 $bnum=1;
61}
62
63if ($opt_a) {
64 open(MANI,"MANIFEST.new") || die "$progname: can't read MANIFEST.new: $!\n";
65 @ARGV = ();
66 while (<MANI>) {
67 chop;
68 ($_) = split(' ');
69 next if -d;
70 push(@ARGV,$_);
71 }
72 close MANI;
73}
74
75foreach $file (@ARGV) {
76 next if ($file =~ /^patchlevel.h$/); # Skip patchlevel.h
77 if (! -f $file) {
78 print "$progname: $file not found.\n";
79 next;
80 }
81 $files = &rcsargs($file);
82 @files = split(' ',$files);
83 $new='';
84 $revs=0;
85 $rlog = `rlog -rlastpat- $files 2>&1`;
86 ($lastpat) = ($rlog =~ /lastpat: ([\d.]+)/);
87 ($revs) = ($rlog =~ /selected revisions: (\d+)/);
88 if (!$revs) {
89 print "$progname: no cil has been done on $file.\n" ;;
90 } elsif ($revs == 1) {
91 ($base) = ($rlog =~ /.*\nrevision\s+(\S+)/);
92 ($a,$b,$c,$d) = split(/\./,$base);
93 if ($d ne '') {
94 if (!$opt_n) {
95 print
96 "$progname: no changes in $file since last patch. (Did you cil it?)\n";
97 next; # Skip file with no changes
98 } else {
99 $new='foo';
100 }
101 } else {
102 $revs=0;
103 $rlog = `rlog -r$revbranch- $files 2>&1`;
104 ($revs) = ($rlog =~ /selected revisions: (\d+)/);
105 if (!$revs) {
106 print
107 "$progname: no changes in $file since base version. (Did you cil it?)\n";
108 next; # Skip file with no changes
109 } else {
110 ($new) = ($rlog =~ /\nrevision\s*(\d+\.\d+\.\d+\.\d+)/);
111 }
112 }
113 } else {
114 ($new) = ($rlog =~ /\nrevision\s*(\d+\.\d+\.\d+\.\d+)/);
115 }
116 if ($new ne '') {
117 ($fname = $file) =~ s|.*/||;
118 $fname = substr($fname, 0, 11); # For filsystems with short names
119 open(PATCH,">>bugs/$fname.$bnum") || die "Can't make patch";
120 print PATCH "\nIndex: $file\n";
121 open(CO,"co -p -rlastpat $files 2>/dev/null |");
122 while (<CO>) {
123 if (/\$Header/ || /\$Id/) {
124 print PATCH "Prereq: $lastpat\n";
125 last;
126 }
127 }
128 close CO;
129 if (!$opt_n) {
130 if ($mydiff eq '') {
131 open(DIFF,"rcsdiff -c -rlastpat -r$new $files |") ||
132 die "$progname: can't fork rcsdiff: $!\n";
133 while (<DIFF>) {
134 if ($. == 1) {s|\*\*\* \S+ |*** $file.old |;}
135 if ($. == 2) {s|--- \S+ |--- $file |;}
136 s|Lock[e]r:.*\$|\$|; # Use [e] to make it safe on itself
137 print PATCH;
138 }
139 close DIFF;
140 system 'rcs', "-Nlastpat:$new", @files;
141 } else {
142 &copyright'expand("co -p -rlastpat $file", "/tmp/pdo$$");
143 &copyright'expand("co -p -r$new $file", "/tmp/pdn$$");
144 open(DIFF, "$mydiff /tmp/pdo$$ /tmp/pdn$$ |") ||
145 die "Can't run $mydiff";
146 while (<DIFF>) { # Contextual or unified diff
147 if ($. == 1) {
148 s|\*\*\* \S+ |*** $file.old | ||
149 s|--- \S+ |--- $file.old |;
150 }
151 if ($. == 2) {
152 s|--- \S+ |--- $file | ||
153 s|\+\+\+ \S+ |+++ $file |;
154 }
155 s|Lock[e]r:.*\$|\$|; # Remove locker mark
156 print PATCH;
157 }
158 close DIFF;
159 system 'rcs', "-Nlastpat:$new", @files;
160 unlink "/tmp/pdn$$", "/tmp/pdo$$";
161 }
162 } else {
163 if ($mydiff eq '') {
164 open(DIFF,"rcsdiff -c -rlastpat $files |") ||
165 die "Can't run rcsdiff";
166 while (<DIFF>) {
167 if ($. == 1) {s|\*\*\* \S+ |*** $file.old |;}
168 if ($. == 2) {s|--- \S+ |--- $file |;}
169 s|Lock[e]r:.*\$|\$|; # Remove locker mark
170 print PATCH;
171 }
172 close DIFF;
173 } else {
174 system "co -p -rlastpat $files >/tmp/pdo$$";
175 system "cp $file /tmp/pdn$$";
176 open(DIFF, "$mydiff /tmp/pdo$$ /tmp/pdn$$ |") ||
177 die "$progname: can't fork $mydiff: $!\n";
178 while (<DIFF>) {
179 # Contextual or unified diff
180 if ($. == 1) {
181 s|\*\*\* \S+ |*** $file.old |;
182 s|--- \S+ |--- $file.old |;
183 }
184 if ($. == 2) {
185 s|--- \S+ |--- $file |;
186 s|\+\+\+ \S+ |+++ $file |;
187 }
188 s|Lock[e]r:.*\$|\$|; # Remove locker mark
189 print PATCH;
190 }
191 close DIFF;
192 unlink "/tmp/pdn$$", "/tmp/pdo$$";
193 }
194 }
195 }
196}
197
198sub usage {
199 print STDERR <<EOM;
200Usage: $progname [-ahnV] [filelist]
201 -a : all the files in MANIFEST.new
202 -h : print this message and exit
203 -n : non update mode
204 -V : print version number and exit
205EOM
206 exit 1;
207}
208
209sub readpackage {
210 if (! -f '.package') {
211 if (
212 -f '../.package' ||
213 -f '../../.package' ||
214 -f '../../../.package' ||
215 -f '../../../../.package'
216 ) {
217 die "Run in top level directory only.\n";
218 } else {
219 die "No .package file! Run packinit.\n";
220 }
221 }
222 open(PACKAGE,'.package');
223 while (<PACKAGE>) {
224 next if /^:/;
225 next if /^#/;
226 if (($var,$val) = /^\s*(\w+)=(.*)/) {
227 $val = "\"$val\"" unless $val =~ /^['"]/;
228 eval "\$$var = $val;";
229 }
230 }
231 close PACKAGE;
232}
233
234sub rcsargs {
235 local($result) = '';
236 local($_);
237 while ($_ = shift(@_)) {
238 if ($_ =~ /^-/) {
239 $result .= $_ . ' ';
240 } elsif ($#_ >= 0 && do equiv($_,$_[0])) {
241 $result .= $_ . ' ' . $_[0] . ' ';
242 shift(@_);
243 } else {
244 $result .= $_ . ' ' . do other($_) . ' ';
245 }
246 }
247 $result;
248}
249
250sub equiv {
251 local($s1, $s2) = @_;
252 $s1 =~ s|.*/||;
253 $s2 =~ s|.*/||;
254 if ($s1 eq $s2) {
255 0;
256 } elsif ($s1 =~ s/$RCSEXT$// || $s2 =~ s/$RCSEXT$//) {
257 $s1 eq $s2;
258 } else {
259 0;
260 }
261}
262
263sub other {
264 local($s1) = @_;
265 ($dir,$file) = ('./',$s1) unless local($dir,$file) = ($s1 =~ m|(.*/)(.*)|);
266 $dir = $TOPDIR . $dir if -d $TOPDIR . "$dir/RCS";
267 local($wasrcs) = ($file =~ s/$RCSEXT$//);
268 if ($wasrcs) {
269 `mkdir $dir` unless -d $dir;
270 $dir =~ s|RCS/||;
271 } else {
272 $dir .= 'RCS/';
273 `mkdir $dir` unless -d $dir;
274 $file .= $RCSEXT;
275 }
276 "$dir$file";
277}
278
279package copyright;
280
281# Read in copyright file
282sub init {
283 local($file) = @_; # Copyright file
284 undef @copyright;
285 open(COPYRIGHT, $file) || die "Can't open $file: $!\n";
286 chop(@copyright = <COPYRIGHT>);
287 close COPYRIGHT;
288}
289
290# Reset the automaton for a new file.
291sub reset {
292 $copyright_seen = @copyright ? 0 : 1;
293 $marker_seen = 0;
294}
295
296# Filter file, line by line, and expand the copyright string. The @COPYRIGHT@
297# symbol may be preceded by some random comment. A leader can be defined and
298# will be pre-pended to all the input lines.
299sub filter {
300 local($line, $leader) = @_; # Leader is optional
301 return $leader . $line if $copyright_seen || $marker_seen;
302 $marker_seen = 1 if $line =~ /\$Log[:\$]/;
303 $copyright_seen = 1 if $line =~ /\@COPYRIGHT\@/;
304 return $leader . $line unless $copyright_seen;
305 local($comment, $trailer) = $line =~ /^(.*)\@COPYRIGHT\@\s*(.*)/;
306 $comment = $leader . $comment;
307 $comment . join("\n$comment", @copyright) . "\n";
308}
309
310# Filter output of $cmd redirected into $file by expanding copyright, if any.
311sub expand {
312 local($cmd, $file) = @_;
313 if (@copyright) {
314 open(CMD,"$cmd|") || die "Can't start '$cmd': $!\n";
315 open(OUT, ">$file") || die "Can't create $file: $!\n";
316 &reset;
317 local($_);
318 while (<CMD>) {
319 print OUT &filter($_);
320 }
321 close OUT;
322 close CMD;
323 } else {
324 system "$cmd > $file";
325 die "Command '$cmd' failed!" if $?;
326 }
327}
328
329package main;
330
331# Perform ~name expansion ala ksh...
332# (banish csh from your vocabulary ;-)
333sub tilda_expand {
334 local($path) = @_;
335 return $path unless $path =~ /^~/;
336 $path =~ s:^~([^/]+):(getpwnam($1))[$[+7]:e; # ~name
337 $path =~ s:^~:$ENV{'HOME'} || (getpwuid($<))[$[+7]:e; # ~
338 $path;
339}
340
341# Set up profile components into %Profile, add any profile-supplied options
342# into @ARGV and return the command invocation name.
343sub profile {
344 local($profile) = &tilda_expand($ENV{'DIST'} || '~/.dist_profile');
345 local($me) = $0; # Command name
346 $me =~ s|.*/(.*)|$1|; # Keep only base name
347 return $me unless -s $profile;
348 local(*PROFILE); # Local file descriptor
349 local($options) = ''; # Options we get back from profile
350 unless (open(PROFILE, $profile)) {
351 warn "$me: cannot open $profile: $!\n";
352 return;
353 }
354 local($_);
355 local($component);
356 while (<PROFILE>) {
357 next if /^\s*#/; # Skip comments
358 next unless /^$me/o;
359 if (s/^$me://o) { # progname: options
360 chop;
361 $options .= $_; # Merge options if more than one line
362 }
363 elsif (s/^$me-([^:]+)://o) { # progname-component: value
364 $component = $1;
365 chop;
366 s/^\s+//; # Trim leading and trailing spaces
367 s/\s+$//;
368 $Profile{$component} = $_;
369 }
370 }
371 close PROFILE;
372 return unless $options;
373 require 'shellwords.pl';
374 local(@opts);
375 eval '@opts = &shellwords($options)'; # Protect against mismatched quotes
376 unshift(@ARGV, @opts);
377 return $me; # Return our invocation name
378}
379