#!/usr/bin/perl eval "exec perl -S $0 $*" if $running_under_some_shell; # $Id: patcol.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: patcol.SH,v $ # Revision 3.0.1.3 1994/01/24 14:30:25 ram # patch16: now prefix error messages with program's name # patch16: added ~/.dist_profile awareness # # Revision 3.0.1.2 1993/08/24 12:16:30 ram # patch3: new -S option for snapshot check-outs # # Revision 3.0.1.1 1993/08/19 06:42:34 ram # patch1: leading config.sh searching was not aborting properly # # Revision 3.0 1993/08/18 12:10:42 ram # Baseline for dist 3.0 netwide release. # $version = '3.5'; $patchlevel = '0'; $progname = &profile; # Read ~/.dist_profile require 'getopts.pl'; &usage unless $#ARGV >= 0; &usage unless &Getopts("acd:f:hnmsCRS:V"); if ($opt_V) { print STDERR "$progname $version PL$patchlevel\n"; exit 0; } elsif ($opt_h) { &usage; } $RCSEXT = ',v' unless $RCSEXT; $NEWMANI = 'MANIFEST.new' unless $NEWMANI = $opt_f; chop($pwd = `pwd`) unless -f '.package'; until (-f '.package') { die "$progname: no .package file! Run packinit.\n" unless $pwd; chdir '..' || die "$progname: can't cd ..: $!"; $pwd =~ s|(.*)/(.*)|$1|; $prefix = $2 . '/' . $prefix; } if ($prefix) { for (@ARGV) { s/^/$prefix/ unless m|^[-/]|; } } # We now are at the top level &readpackage; undef $opt_C unless -f $copyright; ©right'init($copyright) if $opt_C; &makedir($opt_d) if $opt_d; undef $opt_c unless $opt_d; # Disable -c if not -d undef $opt_R unless $opt_d; # Disable -R if not -d push(@sw, '-q') if $opt_s; # Let RCS work quietly if ($opt_n) { &newer; # Look for files newer than patchlevel.h } elsif ($opt_a) { open(MANI, $NEWMANI) || die "No $NEWMANI found.\n"; @ARGV = (); while () { s|^\./||; next if m|^patchlevel.h| && !$opt_d; # This file is built by hand chop; ($_) = split(' '); next if -d; push(@ARGV,$_); } close MANI; } elsif ($opt_m) { open(MODS,"bugs/.mods$bnum") || die "$progname: no modification found.\n"; @ARGV = (); while () { next if m|^patchlevel.h$|; # This file is built by hand chop; ($_) = split(' '); next if -d; push(@ARGV,$_); } close MODS; } elsif ($opt_S) { &readsnapshot($opt_S); foreach $file (sort keys %Snap) { push(@ARGV, $file); } } # Now loop over each file specified, doing a 'co -l' foreach $file (@ARGV) { if ($opt_c && -f $file) { ©_file($file, $opt_d); next; } # Continue only if file does not exist or option -d was used. if (! -f $file || $opt_d) { $files = &rcsargs($file); @files = split(' ', $files); if ($opt_S && ($rev = $Snap{$file}) ne '') { # Use snapshot file &col($rev); next; } $rlog = `rlog -rlastpat- $files 2>&1`; ($revs) = ($rlog =~ /selected revisions: (\d+)/); if (!$revs) { if ($opt_d) { ©_file($file, $opt_d); } else { print STDERR "$progname: $file has never been checked in\n"; } } else { # Look whether there is a branch if ($revs == 1) { $rlog = `rlog -r$revbranch $files 2>&1`; ($revs) = ($rlog =~ /selected revisions: (\d+)/); $revs++; # add the base revision (trunk) } if ($revs == 1) { &col($baserev); } else { &col($revbranch); } } } } # Run co -l on @files, unlock file if it fails and retry. # If '-d' option was used, we check out in the specified # directory, after having made all the necessary directories # in the path name (which should be relative to the top). sub col { local($rev) = shift; # Revision to be checked out. if (! $opt_d) { if (system 'co', "-l$rev", @sw, @files) { print "$progname: unlocking and trying again...\n" unless $opt_s; system 'rcs', '-u', @sw, @files; system 'co', "-l$rev", @sw, @files unless $?; } } else { local($name) = $files[0]; # First element is file name $_ = $name; s|(.*)/.*|\1| && &makedir("$opt_d/$_"); if ($opt_C) { ©right'expand("co -p @sw -r$rev $files[1]", "$opt_d/$name"); } else { system "co -p -r$rev @sw $files[1] > $opt_d/$name"; } system 'perl', '-pi', '-e', 's|Lock[e]r:.*\$|\$|;', "$opt_d/$name" if $opt_R; # If RCS file has x bits set, add them on new file -x $files[1] && chmod(0755, "$opt_d/$name"); } } # Copy file into directory, eventually performing copyright expansion... sub copy_file { local($file, $dir) = @_; local($base) = $file =~ m|^(.*)/.*|; &makedir("$dir/$base"); if ($opt_C) { ©right'expand("cat $file", "$dir/$file"); } else { system 'cp', "$file", "$dir/$file"; } system 'perl', '-pi', '-e', 's|Lock[e]r:.*\$|\$|;', "$dir/$file" if $opt_R; -x $file && chmod(0755, "$dir/$file"); print "$progname: $file has been copied\n" unless $opt_s; } sub usage { print STDERR <.newer") || die "Can't create .newer.\n"; open(MANI,"MANIFEST.new"); while () { ($name,$foo) = split; $mani{$name} = 1; } close MANI; while () { s|^\./||; chop; next if m|^MANIFEST|; next if m|^PACKLIST$|; if (!$mani{$_}) { next if m|^MANIFEST.new$|; next if m|^Changes$|; next if m|^Wanted$|; next if m|^.package$|; next if m|^bugs|; next if m|^users$|; next if m|^UU/|; next if m|^RCS/|; next if m|/RCS/|; next if m|^config.sh$|; next if m|/config.sh$|; next if m|^make.out$|; next if m|/make.out$|; next if m|^all$|; next if m|/all$|; next if m|^core$|; next if m|/core$|; next if m|^toto|; next if m|/toto|; next if m|^\.|; next if m|/\.|; next if m|\.o$|; next if m|\.old$|; next if m|\.orig$|; next if m|~$|; next if $mani{$_ . ".SH"}; next if m|(.*)\.c$| && $mani{$1 . ".y"}; next if m|(.*)\.c$| && $mani{$1 . ".l"}; next if (-x $_ && !m|^Configure$|); } print NEWER $_,"\n"; } close FIND; close NEWER; print "Please remove unwanted files...\n"; sleep(2); system '${EDITOR-vi} .newer'; die "Aborted.\n" unless -s '.newer' > 1; @ARGV = split(' ',`cat .newer`); } sub readpackage { if (! -f '.package') { if ( -f '../.package' || -f '../../.package' || -f '../../../.package' || -f '../../../../.package' ) { die "Run in top level directory only.\n"; } else { die "No .package file! Run packinit.\n"; } } open(PACKAGE,'.package'); while () { 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; # Make directories for files # E.g, for /usr/lib/perl/foo, it will check for all the # directories /usr, /usr/lib, /usr/lib/perl and make # them if they do not exist. sub makedir { local($_) = shift; local($dir) = $_; if (!-d && $_ ne '') { # Make dirname first do makedir($_) if s|(.*)/.*|\1|; mkdir($dir, 0700) if ! -d $dir; } } # Read snapshot file and build %Snap, indexed by file name -> RCS revision sub readsnapshot { local($snap) = @_; open(SNAP, $snap) || warn "Can't open $snap: $!\n"; local($_); local($file, $rev); while () { next if /^#/; ($file, $rev) = split; $Snap{$file} = "$rev"; } close SNAP; } # 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 }