#!/usr/bin/perl eval "exec perl -S $0 $*" if $running_under_some_shell; # $Id: patnotify.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. # # $Log: patnotify.SH,v $ # Revision 3.0.1.9 1997/02/28 16:33:35 ram # patch61: let them know the patch priority and description # # Revision 3.0.1.8 1995/09/25 09:21:43 ram # patch59: now tells users how to directly request for mailed patches # # Revision 3.0.1.7 1994/10/29 16:43:19 ram # patch36: added various escapes in strings for perl5 support # # Revision 3.0.1.6 1994/06/20 07:11:47 ram # patch30: patnotify now includes the e-mail address for requests # # Revision 3.0.1.5 1994/01/24 14:31:48 ram # patch16: now prefix error messages with program's name # patch16: don't feed mailer with more than 50 addresses at a time # patch16: added ~/.dist_profile awareness # # Revision 3.0.1.4 1993/08/27 14:40:42 ram # patch7: two files were wrongly appended to patsend instead # # Revision 3.0.1.3 1993/08/25 14:07:43 ram # patch6: now asks for recipient list edition by default # patch6: new -q option to suppress that # # Revision 3.0.1.2 1993/08/24 12:48:03 ram # patch5: fixed fatal typo in here document # # Revision 3.0.1.1 1993/08/24 12:19:11 ram # patch3: created # $defeditor='/usr/bin/vi'; $version = '3.5'; $patchlevel = '0'; $mailer = '/usr/sbin/sendmail'; $progname = &profile; # Read ~/.dist_profile require 'getopts.pl'; &usage unless &Getopts("hquV"); if ($opt_V) { print STDERR "$progname $version PL$patchlevel\n"; exit 0; } elsif ($opt_h) { &usage; } chdir '..' if -d '../bugs'; &readpackage; &readusers if $opt_u; $dest = join(' ', @ARGV); $dest .= " $notify" if $opt_u; &usage unless $dest; # Offer to edit the address list unless -q unless ($opt_q) { select((select(STDOUT), $| = 1)[0]); print "Do you wish to edit the address list? [y] "; $ans = ; unless ($ans =~ /^n/i) { @to = split(' ', $dest); &listedit(*to); $dest = join(' ', @to); } } if (-f 'patchlevel.h') { open(PL,"patchlevel.h") || die "$progname: can't open patchlevel.h: $!\n"; while () { if (/^#define\s+PATCHLEVEL\s+(\d+)/) { $last = $1; } } die "$progname: malformed patchlevel.h file.\n" if $last eq ''; } else { die "$progname: no patchlevel.h.\n"; } @patches = &patseq($last); # Compute patches sequence $lastpat = pop(@patches); warn "$progname: missing last .logs and .mods files\n" if $lastpat eq ''; $missing = $last - $lastpat + 1; $these = $missing == 1 ? 'this' : 'these'; $patches = $missing == 1 ? 'patch' : 'patches'; $through = $missing == 1 ? $lastpat : "$lastpat thru " . ($lastpat+$missing-1); $have = $missing == 1 ? 'has' : 'have'; $patlist = "$lastpat-"; # They'll get everything up to the end ($Patches = $patches) =~ s/^p/P/; $opt = ($mailer =~ /sendmail/) ? '-odq' : ''; chdir 'bugs' || die "$progname: can't cd to bugs: $!\n"; # Find out priority of last patch set $priority = 'UNKNOWN'; open(PATCH, "patch$lastpat") || die "$progname: can't open patch #$lastpat: $!\n"; while () { /^Priority:\s*(\S+)\s*$/ && ($priority = $1); } close PATCH; # Look for the .clog description and prepare the patch description # for inclusion in the notification, so that they can figure out whether # they really need that patch set. if (-f ".clog$lastpat") { open(LOGS, ".clog$lastpat") || die "$progname: can't open .clog$lastpat: $!\n"; $_ = . . ; # Skip first three lines $clog = ''; while () { $clog .= $_; } close LOGS; } else { warn "$progname: missing last .clog file in bugs directory\n"; } print "$progname: sending notification of $missing new $patches to $dest...\n"; fork && exit; # I hate broken mailers! Bust it up into smaller groups of people... @dest = split(' ', $dest); while (@smalldest = splice(@dest, 0, 50)) { $to = join(', ', @smalldest); # Sensible To: for sendmail $smalldest = join(' ', @smalldest); open(MAILER, "|$mailer $opt $smalldest") || die "$progname: can't fork $mailer: $!\n"; print MAILER "To: $to Subject: $Patches $through for $package version $baserev $have been released. Precedence: bulk X-Mailer: dist [version $version PL$patchlevel] This is just a quick note to let you know that $package version $baserev has been recently upgraded and that $patches $through $have been released. If you are actively using $package, I strongly suggest you upgrade by applying $these $patches, whose priority is $priority. You can fetch $these $patches automatically by sending me the following mail: Subject: Command \@SH mailpatch - $package $baserev $patlist ^ note the c And if you wish to have future patches mailed directly to you, you can add: \@SH package - $package $baserev - mailpatches If you are not interested in receiving any information about future patches, please send me the following mail: Subject: Command \@SH package - $package $baserev "; print MAILER " Following is the $patches description: $clog" if $clog; print MAILER " -- $progname speaking for $maintname <$maintloc>. "; close MAILER; } sub usage { print STDERR <) { next if /^:/; next if /^#/; if (($var,$val) = /^\s*(\w+)=(.*)/) { $val = "\"$val\"" unless $val =~ /^['"]/; eval "\$$var = $val;"; } } close PACKAGE; } sub readusers { return unless open(USERS, 'users'); local($_); local($status, $name, $pl); while () { next if /^#/; chop if /\n$/; # Emacs may leave final line without \n ($status, $pl, $name) = split; # Handle oldstyle two-field user file format (PL13 and before) $name = $pl unless defined $name; if ($status eq 'M') { $recipients = $recipients ? "$recipients $name" : $name; } elsif ($status eq 'N') { $notify = $notify ? "$notify $name" : $name; } } close USERS; } # Compute patch sequence by scanning the bugs directory and looking for # .logs and/or .mods files to determine what was the last issued patch series. sub patseq { local($cur) = @_; # Current patch level local(@seq); # Issued patch sequence local($i); for ($i = 1; $i <= $cur; $i++) { push(@seq, $i) if -f "bugs/.logs$i" || -f "bugs/.mods$i"; } @seq; } # Compute suitable editor name sub geteditor { local($editor) = $ENV{'VISUAL'}; $editor = $ENV{'EDITOR'} unless $editor; $editor = $defeditor unless $editor; $editor = 'vi' unless $editor; $editor; } # Allow user to inplace-edit a list of items held in an array sub listedit { local(*list) = @_; local($tmp) = "/tmp/dist.$$"; local($editor) = &geteditor; open(TMP, ">$tmp") || die "Can't create $tmp: $!\n"; foreach $item (@list) { print TMP $item, "\n"; } close TMP; system "$editor $tmp"; open(TMP, "$tmp") || die "Can't reopen $tmp: $!\n"; chop(@list = ); close TMP; unlink $tmp; } # 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 }