#!/usr/bin/perl use strict; use Text::Wrap; my %output; my $text; my %stash; use Getopt::Long; my %opts = ( c => 'CHANGES.new', n => 'NEWS.new', ); LocalGetOptions(\%opts, ['s|start-regexp=s','A regexp to look for in files to start converting at'], ['e|end-regexp=s','A regexp to look for in files to end converting at'], "", ['c|changes-file=s','A file to save CHANGES entries to'], ['n|news-file=s','A file to save NEWS entries to'], ['d|debug-line=s', 'Debugging output for lines matching STRING'], "", ['GUI:otherargs_text','Input files to parse'], ); my $maybecontinue = 0; my $lasttext = ""; my $lastfile; my $lastcomponent; foreach my $argv (@ARGV) { open(I, $argv); if ($opts{'s'}) { while () { last if (/$opts{'s'}/o); } } while () { my ($file, $component, $patbug, $nums, $text); last if ($opts{'e'} && /$opts{e}/o); print STDERR "here: $_" if ($opts{'d'} && /$opts{'d'}/o); # don't use this: # FILE: BUGS: 123,456: text ($file, $patbug, $nums, $text) = /(NEWS|CHANGES):\s*-*\s*\[*(BUG|PATCH)(?:ES|S|):*\s*([\d,\s*]*)\]*:*\s*-*\s*(.*)/; print STDERR " 1:$file, $component, $patbug, $nums, $text\n" if ($opts{'d'} && /$opts{'d'}/o); # or this: # FILE: component - text ($file, $component, $text) = /(NEWS|CHANGES):\s*(\w+)\s*-+\s*(.*)/ if (!$file); print STDERR " 2:$file, $component, $patbug, $nums, $text\n" if ($opts{'d'} && /$opts{'d'}/o); # what you should use: # FILE: component: BUGS: 123,456: text # # or # FILE: component: PATCH: 123,456: from someone text # FILE: component: PATCH: 123,456: from "someone long" text ($file, $component, $patbug, $nums, $text) = /(NEWS|CHANGES):\s*([^:]+):\s*-*\s*\[*(BUG|PATCH)(?:ES|S):*\s*([\d,\s*]*)\]*:*\s*-*\s*(?:from ["'][^"]+["']|from \w+|):*\s*(.*)/ if (!$file); print STDERR " 3:$file, $component, $patbug, $nums, $text\n" if ($opts{'d'} && /$opts{'d'}/o); # or at least: # FILE: component: text ($file, $component, $text) = /(NEWS|CHANGES):\s*([^:]+):\s*-*\s*(.*)/ if (!$file); print STDERR " 4:$file, $component, $patbug, $nums, $text\n" if ($opts{'d'} && /$opts{'d'}/o); # component left out # FILE: [BUGS: 123,456]: text ($file, $patbug, $nums, $text) = /(NEWS|CHANGES):\s*\[*(BUG|PATCH)*(?:ES|S|):*\s*([\d,\s*]*)\]*:*\s*-*\s*(.*)/ if (!$file); print STDERR " 5:$file, $component, $patbug, $nums, $text\n" if ($opts{'d'} && /$opts{'d'}/o); if ($opts{'d'} && /$opts{'d'}/o) { my $bogus = 1; # breakable point } if (!$file && $maybecontinue) { if (/^\s*(.+)$/) { $text = $1; $file = $lastfile; $component = $lastcomponent; # we're going to re-add these pop @{$output{$opts{'c'}}{$component}}; pop @{$output{$opts{'n'}}{$component}} if ($file eq 'NEWS'); } else { $maybecontinue = 0; $lasttext = ""; next; } } elsif (!$file) { next; } next if (exists($stash{$text})); $stash{$text} = 1; $component = "unspecified" if (!$component); if ($patbug) { $lasttext .= " [$patbug $nums]: $text"; } else { $lasttext .= " $text"; } $lasttext =~ s/^ //; # get rid of leading spaces $lasttext =~ s/^([a-z])/uc($1)/e; # capitalize the first letter $text = wrap(" - "," ","$lasttext") . "\n"; # # Assist with displaying categories in a sensible order # snmplib first # snmpd/snmp{apps} # various other # O/S specific (relies on upper case) # $component =~ s/^snmplib/00snmplib/; $component =~ s/^snmp/0snmp/; $component =~ s/^agent/0snmpd/; # Merge "agent" into "snmpd" $component =~ s/^([A-Z])/zz\1/; print STDERR " t:$file, $component, $patbug, $nums, $text\n" if ($opts{'d'} && /$opts{'d'}/o); push @{$output{$opts{'c'}}{$component}}, $text; push @{$output{$opts{'n'}}{$component}}, $text if ($file eq 'NEWS'); $lastfile = $file; $lastcomponent = $component; $maybecontinue = 1; } } # save the news and changes to appropriate files foreach my $f ($opts{'c'}, $opts{'n'}) { my $cat2; open(O,">$f"); foreach my $cat (sort (keys(%{$output{$f}}))) { ($cat2 = $cat) =~ s/^00?|^zz//; print O " $cat2:\n"; print O sort @{$output{$f}{$cat}}; print O "\n"; } close(O); } ####################################################################### # getopt long gui portability code # sub LocalGetOptions { if (eval {require Getopt::GUI::Long;}) { import Getopt::GUI::Long; Getopt::GUI::Long::Configure(qw(display_help no_ignore_case)); return GetOptions(@_); } else { require Getopt::Long; Getopt::Long::Configure(qw(auto_help no_ignore_case)); import Getopt::Long; } GetOptions(LocalOptionsMap(@_)); } sub LocalOptionsMap { my ($st, $cb, @opts) = ((ref($_[0]) eq 'HASH') ? (1, 1, $_[0]) : (0, 2)); for (my $i = $st; $i <= $#_; $i += $cb) { if ($_[$i]) { next if (ref($_[$i]) eq 'ARRAY' && $_[$i][0] =~ /^GUI:/); push @opts, ((ref($_[$i]) eq 'ARRAY') ? $_[$i][0] : $_[$i]); push @opts, $_[$i+1] if ($cb == 2); } } return @opts; }