head	1.2;
access;
symbols
	RELENG_4_11_0_RELEASE:1.1.1.1
	RELENG_4_11:1.1.1.1.0.24
	RELENG_4_11_BP:1.1.1.1
	RELENG_4_10_0_RELEASE:1.1.1.1
	RELENG_4_10:1.1.1.1.0.22
	RELENG_4_10_BP:1.1.1.1
	RELENG_4_9_0_RELEASE:1.1.1.1
	RELENG_4_9:1.1.1.1.0.20
	RELENG_4_9_BP:1.1.1.1
	RELENG_4_8_0_RELEASE:1.1.1.1
	RELENG_4_8:1.1.1.1.0.18
	RELENG_4_8_BP:1.1.1.1
	RELENG_4_7_0_RELEASE:1.1.1.1
	RELENG_4_7:1.1.1.1.0.16
	RELENG_4_7_BP:1.1.1.1
	RELENG_4_6_2_RELEASE:1.1.1.1
	RELENG_4_6_1_RELEASE:1.1.1.1
	RELENG_4_6_0_RELEASE:1.1.1.1
	RELENG_4_6:1.1.1.1.0.14
	RELENG_4_6_BP:1.1.1.1
	v5_006_01:1.1.1.2
	RELENG_4_5_0_RELEASE:1.1.1.1
	RELENG_4_5:1.1.1.1.0.12
	RELENG_4_5_BP:1.1.1.1
	RELENG_4_4_0_RELEASE:1.1.1.1
	RELENG_4_4:1.1.1.1.0.10
	RELENG_4_4_BP:1.1.1.1
	RELENG_4_3_0_RELEASE:1.1.1.1
	RELENG_4_3:1.1.1.1.0.8
	RELENG_4_3_BP:1.1.1.1
	RELENG_4_2_0_RELEASE:1.1.1.1
	RELENG_4_1_1_RELEASE:1.1.1.1
	PRE_SMPNG:1.1.1.2
	RELENG_4_1_0_RELEASE:1.1.1.1
	v5_006:1.1.1.2
	RELENG_3_5_0_RELEASE:1.1.1.1
	RELENG_4_0_0_RELEASE:1.1.1.1
	RELENG_4:1.1.1.1.0.6
	RELENG_4_BP:1.1.1.1
	RELENG_3_4_0_RELEASE:1.1.1.1
	RELENG_3_3_0_RELEASE:1.1.1.1
	RELENG_3_2_PAO:1.1.1.1.0.4
	RELENG_3_2_PAO_BP:1.1.1.1
	RELENG_3_2_0_RELEASE:1.1.1.1
	v5_005_03:1.1.1.1
	RELENG_3_1_0_RELEASE:1.1.1.1
	RELENG_3:1.1.1.1.0.2
	RELENG_3_BP:1.1.1.1
	RELENG_3_0_0_RELEASE:1.1.1.1
	v5_005_02:1.1.1.1
	LWALL:1.1.1;
locks; strict;
comment	@# @;


1.2
date	2002.05.16.10.07.48;	author markm;	state dead;
branches;
next	1.1;

1.1
date	98.09.09.06.59.49;	author markm;	state Exp;
branches
	1.1.1.1;
next	;

1.1.1.1
date	98.09.09.06.59.49;	author markm;	state Exp;
branches;
next	1.1.1.2;

1.1.1.2
date	2000.06.25.11.03.02;	author markm;	state Exp;
branches;
next	;


desc
@@


1.2
log
@Perl is no longer in base. Long live the port!
@
text
@#!/bin/perl -w
# 
#	patchls - patch listing utility
#
# Input is one or more patchfiles, output is a list of files to be patched.
#
# Copyright (c) 1997 Tim Bunce. All rights reserved.
# This program is free software; you can redistribute it and/or
# modify it under the same terms as Perl itself.
#
# With thanks to Tom Horsley for the seed code.


use Getopt::Std;
use Text::Wrap qw(wrap $columns);
use Text::Tabs qw(expand unexpand);
use strict;
use vars qw($VERSION);

$VERSION = 2.08;

sub usage {
die qq{
  patchls [options] patchfile [ ... ]

    -h     no filename headers (like grep), only the listing.
    -l     no listing (like grep), only the filename headers.
    -i     Invert: for each patched file list which patch files patch it.
    -c     Categorise the patch and sort by category (perl specific).
    -m     print formatted Meta-information (Subject,From,Msg-ID etc).
    -p N   strip N levels of directory Prefix (like patch), else automatic.
    -v     more verbose (-d for noisy debugging).
    -n     give a count of the number of patches applied to a file if >1.
    -f F   only list patches which patch files matching regexp F
           (F has \$ appended unless it contains a /).
    -e     Expect patched files to Exist (relative to current directory)
           Will print warnings for files which don't. Also affects -4 option.
  other options for special uses:
    -I     just gather and display summary Information about the patches.
    -4     write to stdout the PerForce commands to prepare for patching.
    -5     like -4 but add "|| exit 1" after each command
    -M T   Like -m but only output listed meta tags (eg -M 'Title From')
    -W N   set wrap width to N (defaults to 70, use 0 for no wrap)
    -X     list patchfiles that may clash (i.e. patch the same file)

  patchls version $VERSION by Tim Bunce
}
}

$::opt_p = undef;	# undef != 0
$::opt_d = 0;
$::opt_v = 0;
$::opt_m = 0;
$::opt_n = 0;
$::opt_i = 0;
$::opt_h = 0;
$::opt_l = 0;
$::opt_c = 0;
$::opt_f = '';
$::opt_e = 0;

# special purpose options
$::opt_I = 0;
$::opt_4 = 0;	# output PerForce commands to prepare for patching
$::opt_5 = 0;
$::opt_M = '';	# like -m but only output these meta items (-M Title)
$::opt_W = 70;	# set wrap width columns (see Text::Wrap module)
$::opt_C = 0;	# 'Chip' mode (handle from/tags/article/bug files) undocumented
$::opt_X = 0;	# list patchfiles that patch the same file

usage unless @@ARGV;

getopts("dmnihlvecC45Xp:f:IM:W:") or usage;

$columns = $::opt_W || 9999999;

$::opt_m = 1 if $::opt_M;
$::opt_4 = 1 if $::opt_5;
$::opt_i = 1 if $::opt_X;

# see get_meta_info()
my @@show_meta = split(' ', $::opt_M || 'Title From Msg-ID Files');
my %show_meta = map { ($_,1) } @@show_meta;

my %cat_title = (
    'BUILD'	=> 'BUILD PROCESS',
    'CORE'	=> 'CORE LANGUAGE',
    'DOC'	=> 'DOCUMENTATION',
    'LIB'	=> 'LIBRARY',
    'PORT1'	=> 'PORTABILITY - WIN32',
    'PORT2'	=> 'PORTABILITY - GENERAL',
    'TEST'	=> 'TESTS',
    'UTIL'	=> 'UTILITIES',
    'OTHER'	=> 'OTHER CHANGES',
    'EXT'	=> 'EXTENSIONS',
    'UNKNOWN'	=> 'UNKNOWN - NO FILES PATCH',
);


sub get_meta_info {
    my $ls = shift;
    local($_) = shift;
    if (/^From:\s+(.*\S)/i) {;
	my $from = $1;	# temporary measure for Chip Salzenberg
	$from =~ s/chip\@@(atlantic\.net|perlsupport\.com)/chip\@@pobox.com/;
	$from =~ s/\(Tim Bunce\) \(Tim Bunce\)/(Tim Bunce)/;
	$ls->{From}{$from} = 1
    }
    if (/^Subject:\s+(?:Re: )?(.*\S)/i) {
	my $title = $1;
	$title =~ s/\[(PATCH|PERL)[\w\. ]*\]:?//g;
	$title =~ s/\b(PATCH|PERL)[\w\.]*://g;
	$title =~ s/\bRe:\s+/ /g;
	$title =~ s/\s+/ /g;
	$title =~ s/^\s*(.*?)\s*$/$1/g;
	$ls->{Title}{$title} = 1;
    }
    $ls->{'Msg-ID'}{$1}=1 if /^Message-Id:\s+(.*\S)/i;
    $ls->{Date}{$1}=1     if /^Date:\s+(.*\S)/i;
    $ls->{$1}{$2}=1       if $::opt_M && /^([-\w]+):\s+(.*\S)/;
}


# Style 1:
#	*** perl-5.004/embed.h  Sat May 10 03:39:32 1997
#	--- perl-5.004.fixed/embed.h    Thu May 29 19:48:46 1997
#	***************
#	*** 308,313 ****
#	--- 308,314 ----
#
# Style 2:
#	--- perl5.004001/mg.c   Sun Jun 08 12:26:24 1997
#	+++ perl5.004-bc/mg.c   Sun Jun 08 11:56:08 1997
#	@@@@ -656,9 +656,27 @@@@
# or (rcs, note the different date format)
#	--- 1.18	1997/05/23 19:22:04
#	+++ ./pod/perlembed.pod	1997/06/03 21:41:38
#
# Variation:
#	Index: embed.h

my %ls;

my $in;
my $ls;
my $prevline = '';
my $prevtype = '';
my (@@removed, @@added);
my $prologue = 1;	# assume prologue till patch or /^exit\b/ seen


foreach my $argv (@@ARGV) {
    $in = $argv;
    unless (open F, "<$in") {
	warn "Unable to open $in: $!\n";
	next;
    }
    print "Reading $in...\n" if $::opt_v and @@ARGV > 1;
    $ls = $ls{$in} ||= { is_in => 1, in => $in };
    my $type;
    while (<F>) {
	unless (/^([-+*]{3}) / || /^(Index):/) {
	    # not an interesting patch line
	    # but possibly meta-information or prologue
	    if ($prologue) {
		push @@added, $1     if /^touch\s+(\S+)/;
		push @@removed, $1   if /^rm\s+(?:-f)?\s*(\S+)/;
		$prologue = 0       if /^exit\b/;
	    }
	    get_meta_info($ls, $_) if $::opt_m;
	    next;
	}
	$type = $1;
	next if /^--- [0-9,]+ ----$/ || /^\*\*\* [0-9,]+ \*\*\*\*$/;
	$prologue = 0;

	print "Last: $prevline","This: ${_}Got:  $type\n\n" if $::opt_d;

	# Some patches have Index lines but not diff headers
	# Patch copes with this, so must we. It's also handy for
	# documenting manual changes by simply adding Index: lines
	# to the file which describes the problem being fixed.
	if (/^Index:\s+(.*)/) {
	    my $f;
	    foreach $f (split(/ /, $1)) { add_file($ls, $f) }
	    next;
	}

	if (	($type eq '---' and $prevtype eq '***')	# Style 1
	    or	($type eq '+++' and $prevtype eq '---')	# Style 2
	) {
	    if (/^[-+*]{3} (\S+)\s*(.*?\d\d:\d\d:\d\d)?/) {	# double check
		add_file($ls, $1);
	    }
	    else {
		warn "$in $.: parse error (prev $prevtype, type $type)\n$prevline$_";
	    }
	}
    }
    continue {
	$prevline = $_;
	$prevtype = $type || '';
	$type = '';
    }

    # special mode for patch sets from Chip
    if ($in =~ m:[\\/]patch$:) {
	my $is_chip;
	my $chip;
	my $dir; ($dir = $in) =~ s:[\\/]patch$::;
	if (!$ls->{From} && (open(CHIP,"$dir/article") || open(CHIP,"$dir/bug"))) {
	    get_meta_info($ls, $_) while (<CHIP>);
	    $is_chip = 1;
	}
	if (open CHIP,"<$dir/from") {
	    chop($chip = <CHIP>);
	    $ls->{From} = { $chip => 1 };
	    $is_chip = 1;
	}
	if (open CHIP,"<$dir/tag") {
	    chop($chip = <CHIP>);
	    $ls->{Title} = { $chip => 1 };
	    $is_chip = 1;
	}
	$ls->{From} = { "Chip Salzenberg" => 1 } if $is_chip && !$ls->{From};
    }

    # if we don't have a title for -m then use the file name
    $ls->{Title}{$in}=1 if $::opt_m
	and !$ls->{Title} and $ls->{out};

    $ls->{category} = $::opt_c
	? categorize_files([keys %{ $ls->{out} }], $::opt_v) : '';
}
print scalar(@@ARGV)." files read.\n" if $::opt_v and @@ARGV > 1;


# --- Firstly we filter and sort as needed ---

my @@ls  = values %ls;

if ($::opt_f) {		# filter out patches based on -f <regexp>
    $::opt_f .= '$' unless $::opt_f =~ m:/:;
    @@ls = grep {
	my $match = 0;
	if ($_->{is_in}) {
	    my @@out = keys %{ $_->{out} };
	    $match=1 if grep { m/$::opt_f/o } @@out;
	}
	else {
	    $match=1 if $_->{in} =~ m/$::opt_f/o;
	}
	$match;
    } @@ls;
}

@@ls  = sort {
    $a->{category} cmp $b->{category} || $a->{in} cmp $b->{in}
} @@ls;


# --- Handle special modes ---

if ($::opt_4) {
    my $tail = ($::opt_5) ? "|| exit 1" : "";
    print map { "p4 delete $_$tail\n" } @@removed if @@removed;
    print map { "p4 add    $_$tail\n" } @@added   if @@added;
    my @@patches = sort grep { $_->{is_in} } @@ls;
    my @@no_outs = grep { keys %{$_->{out}} == 0 } @@patches;
    warn "Warning: Some files contain no patches:",
	join("\n\t", '', map { $_->{in} } @@no_outs), "\n" if @@no_outs;
    my %patched = map { ($_, 1) } map { keys %{$_->{out}} } @@patches;
    delete @@patched{@@added};
    my @@patched = sort keys %patched;
    foreach(@@patched) {
	my $edit = ($::opt_e && !-f $_) ? "add " : "edit";
	print "p4 $edit   $_$tail\n";
    }
    exit 0 unless $::opt_C;
}


if ($::opt_I) {
    my $n_patches = 0;
    my($in,$out);
    my %all_out;
    my @@no_outs;
    foreach $in (@@ls) {
	next unless $in->{is_in};
	++$n_patches;
	my @@outs = keys %{$in->{out}};
	push @@no_outs, $in unless @@outs;
	@@all_out{@@outs} = ($in->{in}) x @@outs;
    }
    my @@all_out = sort keys %all_out;
    my @@missing = grep { ! -f $_ } @@all_out;
    print "$n_patches patch files patch ".@@all_out." files (".@@missing." missing)\n";
    print @@no_outs." patch files don't contain patches.\n" if @@no_outs;
    print "(use -v to list patches which patch 'missing' files)\n"
	    if (@@missing || @@no_outs) && !$::opt_v;
    if ($::opt_v and @@no_outs) {
	print "Patch files which don't contain patches:\n";
	foreach $out (@@no_outs) {
	    printf "  %-20s\n", $out->{in};
	}
    }
    if ($::opt_v and @@missing) {
	print "Missing files:\n";
	foreach $out (@@missing) {
	    printf "  %-20s\t", $out	unless $::opt_h;
	    print $all_out{$out}	unless $::opt_l;
	    print "\n";
	}
    }
    print "Added files:   @@added\n"   if @@added;
    print "Removed files: @@removed\n" if @@removed;
    exit 0+@@missing;
}

unless ($::opt_c and $::opt_m) {
    foreach $ls (@@ls) {
	next unless ($::opt_i) ? $ls->{is_out} : $ls->{is_in};
	next if $::opt_X and keys %{$ls->{out}} <= 1;
	list_files_by_patch($ls);
    }
}
else {
    my $c = '';
    foreach $ls (@@ls) {
	next unless ($::opt_i) ? $ls->{is_out} : $ls->{is_in};
	print "\n  ------  $cat_title{$ls->{category}}  ------\n"
	    if $ls->{category} ne $c;
	$c = $ls->{category};
	unless ($::opt_i) {
	    list_files_by_patch($ls);
	}
	else {
	    my $out = $ls->{in};
	    print "\n$out patched by:\n";
	    # find all the patches which patch $out and list them
	    my @@p = grep { $_->{out}->{$out} } values %ls;
	    foreach $ls (@@p) {
		list_files_by_patch($ls, '');
	    }
	}
    }
    print "\n";
}

exit 0;


# ---


sub add_file {
    my $ls = shift;
	print "add_file '$_[0]'\n" if $::opt_d;
    my $out = trim_name(shift);

    $ls->{out}->{$out} = 1;

    warn "$out patched but not present\n" if $::opt_e && !-f $out;

    # do the -i inverse as well, even if we're not doing -i
    my $i = $ls{$out} ||= {
	is_out   => 1,
	in       => $out,
	category => $::opt_c ? categorize_files([ $out ], $::opt_v) : '',
    };
    $i->{out}->{$in} = 1;
}


sub trim_name {		# reduce/tidy file paths from diff lines
    my $name = shift;
    $name = "$name ($in)" if $name eq "/dev/null";
    $name =~ s:\\:/:g;	# adjust windows paths
    $name =~ s://:/:g;	# simplify (and make win \\share into absolute path)
    if (defined $::opt_p) {
	# strip on -p levels of directory prefix
	my $dc = $::opt_p;
	$name =~ s:^[^/]+/(.+)$:$1: while $dc-- > 0;
    }
    else {	# try to strip off leading path to perl directory
	# if absolute path, strip down to any *perl* directory first
	$name =~ s:^/.*?perl.*?/::i;
	$name =~ s:.*perl[-_]?5?[._]?[-_a-z0-9.+]*/::i;
	$name =~ s:^\./::;
    }
    return $name;
}


sub list_files_by_patch {
    my($ls, $name) = @@_;
    $name = $ls->{in} unless defined $name;
    my @@meta;
    if ($::opt_m) {
	my $meta;
	foreach $meta (@@show_meta) {
	    next unless $ls->{$meta};
	    my @@list = sort keys %{$ls->{$meta}};
	    push @@meta, sprintf "%7s:  ", $meta;
	    if ($meta eq 'Title') {
		@@list = map { "\"$_\""; } @@list;
		push @@list, "#$1" if $::opt_C && $ls->{in} =~ m:\b(\w\d+)/patch$:;
	    }
	    elsif ($meta eq 'From') {
		# fix-up bizzare addresses from japan and ibm :-)
		foreach(@@list) {
		    s:\W+=?iso.*?<: <:;
		    s/\d\d-\w\w\w-\d{4}\s+\d\d:\S+\s*//;
		}
	    }
	    elsif ($meta eq 'Msg-ID') {
		my %from; # limit long threads to one msg-id per site
		@@list = map {
		    $from{(/@@(.*?)>/ ? $1 : $_)}++ ? () : ($_);
		} @@list;
	    }
	    push @@meta, my_wrap("","          ", join(", ",@@list)."\n");
	}
	$name = "\n$name" if @@meta and $name;
    }
    # don't print the header unless the file contains something interesting
    return if !@@meta and !$ls->{out} and !$::opt_v;
    if ($::opt_l) {	# -l = no listing, just names
	print "$ls->{in}";
	my $n = keys %{ $ls->{out} };
	print " ($n patches)" if $::opt_n and $n>1;
	print "\n";
	return;
    }

    # a twisty maze of little options
    my $cat = ($ls->{category} and !$::opt_m) ? "\t$ls->{category}" : "";
    print "$name$cat: "	unless ($::opt_h and !$::opt_v) or !"$name$cat";
    print join('',"\n",@@meta) if @@meta;

    return if $::opt_m && !$show_meta{Files};
    my @@v = sort PATORDER keys %{ $ls->{out} };
    my $n = @@v;
    my $v = "@@v";
    print $::opt_m ? "  Files:  ".my_wrap("","          ",$v) : $v;
    print " ($n patches)" if $::opt_n and $n>1;
    print "\n";
}


sub my_wrap {
	my $txt = eval { expand(wrap(@@_)) };	# die's on long lines!
    return $txt unless $@@;
	return expand("@@_");
}



sub categorize_files {
    my($files, $verb) = @@_;
    my(%c, $refine);

    foreach (@@$files) {	# assign a score to a file path
	# the order of some of the tests is important
	$c{TEST} += 5,next   if m:^t/:;
	$c{DOC}  += 5,next   if m:^pod/:;
	$c{UTIL} += 10,next  if m:^(utils|x2p|h2pl)/:;
	$c{PORT1}+= 15,next  if m:^win32:;
	$c{PORT2} += 15,next
	    if m:^(cygwin32|os2|plan9|qnx|vms)/:
	    or m:^(hints|Porting|ext/DynaLoader)/:
	    or m:^README\.:;
	$c{EXT}  += 10,next
	    if m:^(ext|lib/ExtUtils)/:;
	$c{LIB}  += 10,next
	    if m:^(lib)/:;
	$c{'CORE'} += 15,next
	    if m:^[^/]+[\._]([chH]|sym|pl)$:;
	$c{BUILD} += 10,next
	    if m:^[A-Z]+$: or m:^[^/]+\.SH$:
	    or m:^(install|configure|configpm):i;
	print "Couldn't categorise $_\n" if $::opt_v;
	$c{OTHER} += 1;
    }
    if (keys %c > 1) {	# sort to find category with highest score
      refine:
	++$refine;
	my @@c = sort { $c{$b} <=> $c{$a} || $a cmp $b } keys %c;
	my @@v = map  { $c{$_} } @@c;
	if (@@v > 1 and $refine <= 1 and "@@v" =~ /^(\d) \1/
		and $c[0] =~ m/^(DOC|TESTS|OTHER)/) { # rare
	    print "Tie, promoting $c[1] over $c[0]\n" if $::opt_d;
	    ++$c{$c[1]};
	    goto refine;
	}
	print "  ".@@$files." patches: ", join(", ", map { "$_: $c{$_}" } @@c),".\n"
	    if $verb;
	return $c[0] || 'OTHER';
    }
    else {
	my($c, $v) = %c;
	$c ||= 'UNKNOWN'; $v ||= 0;
	print "  ".@@$files." patches: $c: $v\n" if $verb;
	return $c;
    }
}


sub PATORDER {		# PATORDER sort by Chip Salzenberg
    my ($i, $j);

    $i = ($a =~ m#^[A-Z]+$#);
    $j = ($b =~ m#^[A-Z]+$#);
    return $j - $i if $i != $j;

    $i = ($a =~ m#configure|hint#i) || ($a =~ m#[S_]H$#);
    $j = ($b =~ m#configure|hint#i) || ($b =~ m#[S_]H$#);
    return $j - $i if $i != $j;

    $i = ($a =~ m#\.pod$#);
    $j = ($b =~ m#\.pod$#);
    return $j - $i if $i != $j;

    $i = ($a =~ m#include/#);
    $j = ($b =~ m#include/#);
    return $j - $i if $i != $j;

    if ((($i = $a) =~ s#/+[^/]*$##)
	&& (($j = $b) =~ s#/+[^/]*$##)) {
	    return $i cmp $j if $i ne $j;
    }

    $i = ($a =~ m#\.h$#);
    $j = ($b =~ m#\.h$#);
    return $j - $i if $i != $j;

    return $a cmp $b;
}

@


1.1
log
@Initial revision
@
text
@@


1.1.1.1
log
@Initial import of Perl5. The king is dead; long live the king!
@
text
@@


1.1.1.2
log
@Vendor import of Perl 5.006
@
text
@d20 1
a20 1
$VERSION = 2.11;
a37 1
    -      Read patch from STDIN
d96 1
a96 1
    'UNKNOWN'	=> 'UNKNOWN - NO FILES PATCHED',
d134 1
a134 5
#	@@@@ .. @@@@
# or for deletions
#	--- perl5.004001/mg.c   Sun Jun 08 12:26:24 1997
#	+++ /dev/null   Sun Jun 08 11:56:08 1997
#	@@@@ ... @@@@
d148 1
a148 1
my (%removed, %added);
d154 1
a154 7
    if (-d $in) {
	warn "Ignored directory $in\n";
	next;
    }
    if ($in eq "-") {
      *F = \*STDIN;
    } elsif (not open F, "<$in") {
d166 2
a167 2
		$added{$1}   = 1    if /^touch\s+(\S+)/;
		$removed{$1} = 1    if /^rm\s+(?:-f)?\s*(\S+)/;
d185 1
a185 1
	    foreach $f (split(/ /, $1)) { add_patched_file($ls, $f) }
d193 1
a193 7
		if ($1 eq "/dev/null") {
		    $prevline =~ /^[-+*]{3} (\S+)\s*/;
		    add_deleted_file($ls, $1);
		}
		else {
		    add_patched_file($ls, $1);
		}
d229 1
a229 1
    $ls->{Title}{"Untitled: $in"}=1 if $::opt_m
d266 2
a267 2
    print map { "p4 delete $_$tail\n" } sort keys %removed if %removed;
    print map { "p4 add    $_$tail\n" } sort keys %added   if %added;
a271 1

d273 1
a273 1
    delete @@patched{keys %added};
a275 1
	next if $removed{$_};
d315 2
a316 2
    print "Added files:   ".join(" ",sort keys %added  )."\n" if %added;
    print "Removed files: ".join(" ",sort keys %removed)."\n" if %removed;
d356 1
a356 1
sub add_patched_file {
d358 2
a359 2
	my $raw_name = shift;
    my $action = shift || 1;	# 1==patched, 2==deleted
d361 1
a361 4
    my $out = trim_name($raw_name);
    print "add_patched_file '$out' ($raw_name, $action)\n" if $::opt_d;

    $ls->{out}->{$out} = $action;
a373 9
sub add_deleted_file {
    my $ls = shift;
	my $raw_name = shift;
    my $out = trim_name($raw_name);
    print "add_deleted_file '$out' ($raw_name)\n" if $::opt_d;
	$removed{$out} = 1;
    #add_patched_file(@@_[0,1], 2);
}

d377 1
d380 1
a380 4
    if ($name eq "/dev/null") {
	# do nothing (XXX but we need a way to record deletions)
    }
    elsif (defined $::opt_p) {
d388 1
a388 1
	$name =~ s:.*(perl|maint)[-_]?5?[._]?[-_a-z0-9.+]*/::i;
d439 1
a439 3
    my $sep = "\n";
    $sep = "" if @@show_meta==1 && $::opt_c && $::opt_h;
    print join('', $sep, @@meta) if @@meta;
d470 1
a470 1
	    if m:^(cygwin|os2|plan9|qnx|vms)/:
@

