3 $SIG{'PIPE'} = 'IGNORE';
8 # called as GIT_EXTERNAL_DIFF script
9 $old = parse_cooking
($ARGV[1]);
10 $new = parse_cooking
($ARGV[4]);
12 # called with old and new
13 $old = parse_cooking
($ARGV[0]);
14 $new = parse_cooking
($ARGV[1]);
16 compare_cooking
($old, $new);
18 ################################################################
20 use File
::Temp
qw(tempfile);
24 my ($a, $b, $force, $soft) = @_;
33 if (!defined $a->[0]) {
34 return map { "$plus$_\n" } map { split(/\n/) } @
{$b};
35 } elsif (!defined $b->[0]) {
36 return map { "$minus$_\n" } map { split(/\n/) } @
{$a};
37 } elsif (join('', @
$a) eq join('', @
$b)) {
39 return map { " $_\n" } map { split(/\n/) } @
{$a};
44 my ($ah, $aname) = tempfile
();
45 my ($bh, $bname) = tempfile
();
58 open(my $fh, "-|", 'diff', "-U$cnt", $aname, $bname);
65 unlink ($aname, $bname);
70 my ($cooking, $name, $desc) = @_;
71 my $section = $cooking->{SECTIONS
}[-1];
73 return if (!defined $name);
75 $desc =~ s/\s+\Z/\n/s;
78 IN_SECTION
=> $section,
82 $cooking->{TOPICS
}{$name} = $topic;
83 push @
{$cooking->{TOPIC_ORDER
}}, $name;
87 my ($cooking, @line) = @_;
89 while (@line && $line[-1] =~ /^\s*$/) {
94 if (!exists $cooking->{SECTIONS
}) {
95 $cooking->{SECTIONS
} = [];
96 $cooking->{TOPICS
} = {};
97 $cooking->{TOPIC_ORDER
} = [];
99 if (!exists $cooking->{HEADER
}) {
100 my $line = join('', @line);
101 $line =~ s/\A.*?\n\n//s;
102 $cooking->{HEADER
} = $line;
105 if (!exists $cooking->{GREETING
}) {
106 $cooking->{GREETING
} = join('', @line);
110 my ($section_name, $topic_name, $topic_desc);
112 if (!defined $section_name && /^\[(.*)\]$/) {
114 push @
{$cooking->{SECTIONS
}}, $section_name;
119 flush_topic
($cooking, $topic_name, $topic_desc);
120 $topic_name = $next_name;
125 flush_topic
($cooking, $topic_name, $topic_desc);
130 print $cooking->{HEADER
};
131 print "-" x
50, "\n";
132 print $cooking->{GREETING
};
133 for my $section_name (@
{$cooking->{SECTIONS
}}) {
134 print "\n", "-" x
50, "\n";
135 print "[$section_name]\n";
136 for my $topic_name (@
{$cooking->{TOPIC_ORDER
}}) {
137 $topic = $cooking->{TOPICS
}{$topic_name};
138 next if ($topic->{IN_SECTION
} ne $section_name);
139 print "\n", $topic->{DESC
};
146 my (%cooking, @current, $fh);
147 open $fh, "<", $filename
148 or die "cannot open $filename: $!";
151 parse_section
(\
%cooking, @current);
158 parse_section
(\
%cooking, @current);
166 print compare_them
($a, $b, 1, 1);
170 # otherwise they both have title.
171 $a = [map { "$_\n" } split(/\n/, join('', @
$a))];
172 $b = [map { "$_\n" } split(/\n/, join('', @
$b))];
173 my $atitle = shift @
$a;
174 my $btitle = shift @
$b;
175 print compare_them
([$atitle], [$btitle], 1);
178 while (@
$a && $a->[-1] !~ /^\s/) {
179 unshift @atail, pop @
$a;
181 while (@
$b && $b->[-1] !~ /^\s/) {
182 unshift @btail, pop @
$b;
184 print compare_them
($a, $b);
185 print compare_them
(\
@atail, \
@btail);
189 my ($fromto, $names, $topics) = @_;
192 for my $name (@
$names) {
193 my $t = $topics->{$name};
194 my ($a, $b, $in, $force);
195 if ($t->{OLD
} && $t->{NEW
}) {
196 $a = [$t->{OLD
}{DESC
}];
197 $b = [$t->{NEW
}{DESC
}];
198 if ($t->{OLD
}{IN_SECTION
} ne $t->{NEW
}{IN_SECTION
}) {
202 $in = "[$t->{NEW}{IN_SECTION}]";
204 } elsif ($t->{OLD
}) {
205 $a = [$t->{OLD
}{DESC
}];
207 $in = "Was in [$t->{OLD}{IN_SECTION}]";
210 $b = [$t->{NEW
}{DESC
}];
211 $in = "[$t->{NEW}{IN_SECTION}]";
213 next if (defined $a->[0] &&
215 $a->[0] eq $b->[0] && !$force);
217 if (!exists $where{$in}) {
221 push @
{$where{$in}}, [$a, $b];
225 for my $in (@where) {
226 my @bag = @
{$where{$in}};
227 if (defined $fromto && $fromto ne '') {
228 print "\n", '-' x
50, "\n$fromto\n";
231 print "\n$in\n" if ($in ne '');
235 compare_topics
($a, $b);
240 sub compare_cooking
{
241 my ($old, $new) = @_;
243 print compare_them
([$old->{HEADER
}], [$new->{HEADER
}]);
244 print compare_them
([$old->{GREETING
}], [$new->{GREETING
}]);
246 my (@sections, %sections, @topics, %topics, @fromto, %fromto);
248 for my $section_name (@
{$old->{SECTIONS
}}, @
{$new->{SECTIONS
}}) {
249 next if (exists $sections{$section_name});
250 $sections{$section_name} = scalar @sections;
251 push @sections, $section_name;
254 my $gone_class = "Gone topics";
255 my $born_class = "Born topics";
256 my $stay_class = "Other topics";
258 push @fromto, $born_class;
259 for my $topic_name (@
{$old->{TOPIC_ORDER
}}, @
{$new->{TOPIC_ORDER
}}) {
260 next if (exists $topics{$topic_name});
261 push @topics, $topic_name;
263 my $oldtopic = $old->{TOPICS
}{$topic_name};
264 my $newtopic = $new->{TOPICS
}{$topic_name};
265 $topics{$topic_name} = +{
269 my $oldsec = $oldtopic->{IN_SECTION
};
270 my $newsec = $newtopic->{IN_SECTION
};
271 if (defined $oldsec && defined $newsec) {
272 if ($oldsec ne $newsec) {
274 "Moved from [$oldsec] to [$newsec]";
275 if (!exists $fromto{$fromto}) {
276 $fromto{$fromto} = [];
277 push @fromto, $fromto;
279 push @
{$fromto{$fromto}}, $topic_name;
281 push @
{$fromto{$stay_class}}, $topic_name;
283 } elsif (defined $oldsec) {
284 push @
{$fromto{$gone_class}}, $topic_name;
286 push @
{$fromto{$born_class}}, $topic_name;
289 push @fromto, $stay_class;
290 push @fromto, $gone_class;
292 for my $fromto (@fromto) {
293 compare_class
($fromto, $fromto{$fromto}, \
%topics);