2 eval "exec perl -S $0 $*"
3 if $running_under_some_shell;
5 # $Id: patmake.SH 20 2008-01-04 23:14:00Z rmanfredi $
7 # Copyright (c) 1991-1997, 2004-2006, Raphael Manfredi
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.
15 # Original Author: Larry Wall <lwall@netlabs.com>
16 # Contribution by: Graham Stoney <greyham@research.canon.oz.au>
18 # $Log: patmake.SH,v $
19 # Revision 3.0.1.5 1995/09/25 09:21:19 ram
20 # patch59: now calls patsend with -i to add more instructions
22 # Revision 3.0.1.4 1994/10/29 16:43:09 ram
23 # patch36: a lot of setup is now performed by patlog
24 # patch36: added various escapes in strings for perl5 support
26 # Revision 3.0.1.3 1994/01/24 14:30:55 ram
27 # patch16: now prefix error messages with program's name
28 # patch16: added ~/.dist_profile awareness
30 # Revision 3.0.1.2 1993/08/24 12:18:59 ram
31 # patch3: now asks for patch mailing/posting after all patches edited
32 # patch3: patch release notification is done via new patnotify
33 # patch3: random cleanup, removed old RCS logs
35 # Revision 3.0.1.1 1993/08/19 06:42:38 ram
36 # patch1: leading config.sh searching was not aborting properly
38 # Revision 3.0 1993/08/18 12:10:45 ram
39 # Baseline for dist 3.0 netwide release.
42 $defeditor='/usr/bin/vi';
45 $mailer = '/usr/sbin/sendmail';
47 $progname = &profile; # Read ~/.dist_profile
49 &usage unless &Getopts("hV");
52 print STDERR "$progname $version PL$patchlevel\n";
61 $FILEOVERHEAD = 40; # Name of files, Index, Prereq
62 $MAXPATSIZE = 50000; # Maximum allowed size for a patch
63 $PATOVERHEAD = 2500; # Litterature
64 $FIRST_PAT = 3000; # Give space for first patch (descriptions)
66 if (-f 'patchlevel.h') {
67 open(PL,"patchlevel.h") || die "$progname: can't open patchlevel.h: $!\n";
69 if (/^#define\s+PATCHLEVEL\s+(\d+)/) {
71 $patchline = $.; # Record PATCHLEVEL line
74 die "$progname: malformed patchlevel.h file.\n" if $last eq '';
83 @ARGV = <[Mm]akefile*>;
87 $mf .= $_ if /^[a-z.]+\s*:/; # Rules in makefile
91 $after .= "\t\tConfigure -ders\n" if -f 'Configure';
92 $after .= "\t\tmake depend\n" if $mf =~ /^depend:/m;
93 $after .= "\t\tmake\n" if $mf;
94 $after .= "\t\tmake test\n" if $mf =~ /^test:/m;
95 $after .= "\t\tmake install\n" if $mf =~ /^install:/m;
96 $after .= "\t\tmake install.man\n" if $mf =~ /^install\.man:/m;
98 chdir 'bugs' if -d 'bugs';
99 die "$progname: patch #$bnum already exists.\n" if -f "patch$bnum";
102 die "$progname: no diff files for patch #$bnum.\n" if
103 $patlist[0] =~ /^\*/ || $patlist[0] eq '';
105 # Whether they asked for a changelog file or not, call patlog.
106 # This will create at least the .pri and .subj and .clog files that we need.
107 # If a changelog file is needed, it will update it and create the necessary
108 # patch before we go on and put all those patches together.
109 # Note that we make use of the '-r' option, in case they have already
110 # run patlog themselves and made the necessary adjustments. Since -r supersedes
111 # -u, it's safe to allow ~/.dist_profile processing anyway.
113 chdir '..' if -d '../bugs';
114 system 'perl', '-S', 'patlog', '-r'; # Must be called from top-level dir
115 chdir 'bugs' if -d 'bugs';
117 @patlist=<*.$bnum>; # Reget it, in case Changes.xx appeared due to patlog
119 # Look for size of each diff file
121 ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,
122 $blksize,$blocks) = stat($_);
126 # Sort the array, biggest sizes first
127 sub revnum { $size{$a} < $size{$b} ? 1 : $size{$a} > $size{$b} ? -1 : 0; }
128 @patlist = sort revnum @patlist;
130 # Put files in a patch
133 # Find the patch in which the current file can go
134 # Divide size by 15 to count the 3 spaces added in front of each line
135 while (($newtot = int($tot[$i] + $size{$_} + $size{$_}/15 + $FILEOVERHEAD)) >
136 $MAXPATSIZE-$PATOVERHEAD-($i == 1 ? $FIRST_PAT : 0) && $tot[$i]) {
139 # Adding $_ to patch $i giving $newtot bytes
140 $tot[$i] = $newtot; # Update size of kit $i
141 $list[$i] .= " $_"; # Add file to the kit $i
144 $numpat = $#list; # Number of patches to generate
147 print "$progname: Warning: generating $numpat patches.\n";
151 $hah = " (hah!)" if $bnum == 1;
152 $patbase = $bnum; # First patch generated
154 open(PRIORITY, ".pri$bnum");
155 chop($priority = <PRIORITY>);
157 $priority = 'LOW' unless $priority;
159 for ($i = 1; $i <= $numpat; $i++) { # For all patches...
160 open(PATCH,">patch$bnum") || die "Can't create patch #$bnum";
163 "System: $package version $baserev
166 print PATCH "Priority: $priority\n" unless $priority eq '';
167 # Print subjects only for first patch
169 open(SUBJECTS, ".subj$bnum");
170 print PATCH while <SUBJECTS>;
173 print PATCH "Subject: patch #$patbase, continued\n";
177 From: $maintname <$maintloc>
181 # Print description and repeat-by only for first patch
183 open(LOGS, ".clog$bnum");
184 $_ = <LOGS>; $_ = <LOGS>; $_ = <LOGS>; # Skip first three lines
185 print PATCH while <LOGS>;
187 print PATCH "Repeat-By: \n";
189 print PATCH "\tSee patch #$patbase.\n\n";
193 Fix: From rn, say \"| patch -p -N -d DIR\", where DIR is your $package source
194 directory. Outside of rn, say \"cd DIR; patch -p -N <thisarticle\".
195 If you don't have the patch program, apply the following by hand,
196 or get patch (version 2.0, latest patchlevel).
200 # Do $after only after last patch
205 "\t\t*** DO NOTHING--INSTALL ALL PATCHES UP THROUGH #%d FIRST ***\n",
206 $patbase + $numpat - 1;
209 If patch indicates that patchlevel is the wrong version, you may need
210 to apply one or more previous patches, or the patch may already
211 have been applied. See the patchlevel.h file to find out what has or
212 has not been applied. In any event, don't continue with the patch.
214 If you are missing previous patches$hah they can be obtained from me:
216 $maintname <$maintloc>
219 if ($mailagent ne 'false') {
221 " If you send a mail message of the following form it will greatly speed
225 \@SH mailpatch PATH $package $baserev LIST
228 where PATH is a return path FROM ME TO YOU either in Internet notation,
229 or in bang notation from some well-known host, and LIST is the number
230 of one or more patches you need, separated by spaces, commas, and/or
231 hyphens. Saying 35- says everything from 35 to the end.
233 To get some more detailed instructions, send me the following mail:
242 " You can also get the patches via anonymous FTP from
246 # Print patchlevel at the top of each patch
251 `echo "#define PATCHLEVEL 1" >patchlevel.h`;
252 `cp /dev/null patchlevel.h.null`;
253 print PATCH `diff -c patchlevel.h.null patchlevel.h`;
254 unlink 'patchlevel.h', 'patchlevel.h.null';
259 ${patchline}c${patchline}
260 < #define PATCHLEVEL $last
262 > #define PATCHLEVEL $bnum
265 $last = $bnum; # Update last patch
266 push(@patset, $bnum); # Record set of generated patch(es)
268 @ARGV = split(' ', $list[$i]);
269 while (<>) { print PATCH; }
270 print PATCH "\n*** End of Patch $bnum ***\n";
273 # Update patchlevel.h file
274 $editor = $ENV{'VISUAL'};
275 $editor = $ENV{'EDITOR'} unless $editor;
276 $editor = $defeditor unless $editor;
277 $editor = 'vi' unless $editor;
278 system $editor, "patch$bnum";
279 if (-s "patch$bnum") {
280 system 'chmod', '-w', "patch$bnum"; # Protect newly created patch
282 `echo "#define PATCHLEVEL 0" >patchlevel.h` unless -f 'patchlevel.h';
283 open(PL,"patchlevel.h") ||
284 die "$progname: can't open patchlevel.h: $!\n";
285 open(PLN,">patchlevel.h+") ||
286 die "$progname: can't create new patchlevel.h: $!\n";
288 if (/^#define\s+PATCHLEVEL\s+(\d+)/) {
290 $bnum++; # Update patch level
291 print PLN "#define PATCHLEVEL $bnum\n";
293 print PLN; # Simply copy other lines
298 `mv -f patchlevel.h+ patchlevel.h`;
299 die "$progname: malformed patchlevel.h file.\n" if $bnum eq '';
302 die "$progname: aborted.\n";
305 chdir 'bugs' || die "$progname: cannot cd to bugs: $!\n";
307 # Find priority for next patch in loop
309 open(PATCH, "patch$bnum") || die "Cannot re-open patch #$bnum !\n";
311 /^Priority:\s*(\S+)\s*$/ && ($priority = $1);
315 $bnum++; # For next patch in loop
318 utime time, time, 'patchlevel.h'; # Reset timestamp on patchlevel
321 $bnum = pop(@patset);
322 $patch = "patch #$bnum";
324 $bmin = shift(@patset);
325 $bmax = pop(@patset);
326 $bnum = "$bmin-$bmax";
327 $patch = "patches #$bmin thru #$bmax";
330 # Post generated patches
332 print "\nDo you wish to post $patch to $newsgroups? [y] ";
334 system 'patpost', $bnum unless $ans =~ /^n/i;
337 # Mail generated patches
340 if (0 == ($recipients =~ tr/ //)) {
341 print "Do you wish to send $patch to $recipients? [y] ";
343 print "The following people are on the recipient list:\n\n";
344 foreach $addr (split(' ', $recipients)) {
347 print "\nDo you wish to send $patch to them? [y] ";
350 system 'patsend', '-i', $bnum, $recipients unless $ans =~ /^n/i;
353 # Copy patches to FTP directory
355 print "\nDo you wish to copy $patch to $ftpdir? [y] ";
357 system 'patftp', $bnum unless $ans =~ /^n/i;
360 # Notify people about it.
363 if (0 == ($notify =~ tr/ //)) {
364 print "Do you wish to notify $notify? [y] ";
366 print "The following people are on the notify list:\n\n";
367 foreach $addr (split(' ', $notify)) {
370 print "\nDo you wish to notify them? [y] ";
373 system 'patnotify', $notify unless $ans =~ /^n/i;
378 Usage: $progname [-hV]
379 -h : print this message and exit
380 -V : print version number and exit
386 if (! -f '.package') {
389 -f '../../.package' ||
390 -f '../../../.package' ||
391 -f '../../../../.package'
393 die "Run in top level directory only.\n";
395 die "No .package file! Run packinit.\n";
398 open(PACKAGE,'.package');
402 if (($var,$val) = /^\s*(\w+)=(.*)/) {
403 $val = "\"$val\"" unless $val =~ /^['"]/;
404 eval "\$$var = $val;";
411 return unless open(USERS, 'users');
413 local($status, $name, $pl);
416 chop if /\n$/; # Emacs may leave final line without \n
417 ($status, $pl, $name) = split;
418 # Handle oldstyle two-field user file format (PL13 and before)
419 $name = $pl unless defined $name;
420 if ($status eq 'M') {
421 $recipients = $recipients ? "$recipients $name" : $name;
422 } elsif ($status eq 'N') {
423 $notify = $notify ? "$notify $name" : $name;
429 # Perform ~name expansion ala ksh...
430 # (banish csh from your vocabulary ;-)
433 return $path unless $path =~ /^~/;
434 $path =~ s:^~([^/]+):(getpwnam($1))[$[+7]:e; # ~name
435 $path =~ s:^~:$ENV{'HOME'} || (getpwuid($<))[$[+7]:e; # ~
439 # Set up profile components into %Profile, add any profile-supplied options
440 # into @ARGV and return the command invocation name.
442 local($profile) = &tilda_expand($ENV{'DIST'} || '~/.dist_profile');
443 local($me) = $0; # Command name
444 $me =~ s|.*/(.*)|$1|; # Keep only base name
445 return $me unless -s $profile;
446 local(*PROFILE); # Local file descriptor
447 local($options) = ''; # Options we get back from profile
448 unless (open(PROFILE, $profile)) {
449 warn "$me: cannot open $profile: $!\n";
455 next if /^\s*#/; # Skip comments
457 if (s/^$me://o) { # progname: options
459 $options .= $_; # Merge options if more than one line
461 elsif (s/^$me-([^:]+)://o) { # progname-component: value
464 s/^\s+//; # Trim leading and trailing spaces
466 $Profile{$component} = $_;
470 return unless $options;
471 require 'shellwords.pl';
473 eval '@opts = &shellwords($options)'; # Protect against mismatched quotes
474 unshift(@ARGV, @opts);
475 return $me; # Return our invocation name