X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=Debbugs%2FStatus.pm;h=f539781495c297c64a54239823411da2a0c328b1;hb=9947e01ea9e792bf12c77405e06ed69ad8751988;hp=cf6918aa86136325b46444df25a7bd946421ff13;hpb=c6987aaf406c1e5df1c9d54c6e49b025df7438b8;p=debbugs.git diff --git a/Debbugs/Status.pm b/Debbugs/Status.pm index cf6918a..f539781 100644 --- a/Debbugs/Status.pm +++ b/Debbugs/Status.pm @@ -33,11 +33,14 @@ status of a particular bug use warnings; use strict; +use feature 'state'; + use vars qw($VERSION $DEBUG %EXPORT_TAGS @EXPORT_OK @EXPORT); -use base qw(Exporter); +use Exporter qw(import); use Params::Validate qw(validate_with :types); -use Debbugs::Common qw(:util :lock :quit :misc :utf8); +use Debbugs::Common qw(:util :lock :quit :misc); +use Debbugs::UTF8; use Debbugs::Config qw(:config); use Debbugs::MIME qw(decode_rfc1522 encode_rfc1522); use Debbugs::Packages qw(makesourceversions make_source_versions getversions get_versions binary_to_source); @@ -48,7 +51,8 @@ use File::Copy qw(copy); use Encode qw(decode encode is_utf8); use Storable qw(dclone); -use List::Util qw(min max); +use List::AllUtils qw(min max uniq); +use DateTime::Format::Pg; use Carp qw(croak); @@ -59,6 +63,7 @@ BEGIN{ @EXPORT = (); %EXPORT_TAGS = (status => [qw(splitpackages get_bug_status buggy bug_archiveable), qw(isstrongseverity bug_presence split_status_fields), + qw(get_bug_statuses), ], read => [qw(readbug read_bug lockreadbug lockreadbugmerge), qw(lock_read_all_merged_bugs), @@ -69,6 +74,7 @@ BEGIN{ qw(removefoundversions removefixedversions) ], hook => [qw(bughook bughook_archive)], + indexdb => [qw(generate_index_db_line)], fields => [qw(%fields)], ); @EXPORT_OK = (); @@ -108,6 +114,7 @@ our %fields = (originator => 'submitter', blockedby => 'blocked-by', unarchived => 'unarchived', summary => 'summary', + outlook => 'outlook', affects => 'affects', ); @@ -161,33 +168,34 @@ sub read_bug{ if (@_ == 1) { unshift @_, 'bug'; } + state $spec = + {bug => {type => SCALAR, + optional => 1, + # something really stupid passes negative bugnumbers + regex => qr/^-?\d+/, + }, + location => {type => SCALAR|UNDEF, + optional => 1, + }, + summary => {type => SCALAR, + optional => 1, + }, + lock => {type => BOOLEAN, + optional => 1, + }, + locks => {type => HASHREF, + optional => 1, + }, + }; my %param = validate_with(params => \@_, - spec => {bug => {type => SCALAR, - optional => 1, - # something really - # stupid passes - # negative bugnumbers - regex => qr/^-?\d+/, - }, - location => {type => SCALAR|UNDEF, - optional => 1, - }, - summary => {type => SCALAR, - optional => 1, - }, - lock => {type => BOOLEAN, - optional => 1, - }, - locks => {type => HASHREF, - optional => 1, - }, - }, + spec => $spec, ); die "One of bug or summary must be passed to read_bug" if not exists $param{bug} and not exists $param{summary}; my $status; my $log; my $location; + my $report; if (not defined $param{summary}) { my $lref; ($lref,$location) = @param{qw(bug location)}; @@ -197,14 +205,18 @@ sub read_bug{ } $status = getbugcomponent($lref, 'summary', $location); $log = getbugcomponent($lref, 'log' , $location); + $report = getbugcomponent($lref, 'report' , $location); return undef unless defined $status; return undef if not -e $status; } else { $status = $param{summary}; $log = $status; + $report = $status; $log =~ s/\.summary$/.log/; + $report =~ s/\.summary$/.report/; ($location) = $status =~ m/(db-h|db|archive)/; + ($param{bug}) = $status =~ m/(\d+)\.summary$/; } if ($param{lock}) { filelock("$config{spool_dir}/lock/$param{bug}",exists $param{locks}?$param{locks}:()); @@ -221,15 +233,19 @@ sub read_bug{ my %data; my @lines; - my $version = 2; + my $version; local $_; while (<$status_fh>) { chomp; push @lines, $_; - $version = $1 if /^Format-Version: ([0-9]+)/i; + if (not defined $version and + /^Format-Version: ([0-9]+)/i + ) { + $version = $1; + } } - + $version = 2 if not defined $version; # Version 3 is the latest format version currently supported. if ($version > 3) { warn "Unsupported status version '$version'"; @@ -239,7 +255,7 @@ sub read_bug{ return undef; } - my %namemap = reverse %fields; + state $namemap = {reverse %fields}; for my $line (@lines) { if ($line =~ /(\S+?): (.*)/) { my ($name, $value) = (lc $1, $2); @@ -247,7 +263,7 @@ sub read_bug{ # or \n in the fields of status. Kill them off here. # [Eventually, this should be superfluous.] $value =~ s/[\r\n]//g; - $data{$namemap{$name}} = $value if exists $namemap{$name}; + $data{$namemap->{$name}} = $value if exists $namemap->{$name}; } } for my $field (keys %fields) { @@ -266,18 +282,34 @@ sub read_bug{ # create the found/fixed hashes which indicate when a # particular version was marked found or marked fixed. @{$data{$field}}{@{$data{"${field}_versions"}}} = - (('') x (@{$data{"${field}_date"}} - @{$data{"${field}_versions"}}), + (('') x (@{$data{"${field}_versions"}} - @{$data{"${field}_date"}}), @{$data{"${field}_date"}}); } my $status_modified = (stat($status))[9]; # Add log last modified time - $data{log_modified} = (stat($log))[9]; + $data{log_modified} = (stat($log))[9] // (stat("${log}.gz"))[9]; + my $report_modified = (stat($report))[9] // $data{log_modified}; $data{last_modified} = max($status_modified,$data{log_modified}); + # if the date isn't set (ancient bug), use the smallest of any of the modified + if (not defined $data{date} or not length($data{date})) { + $data{date} = min($report_modified,$status_modified,$data{log_modified}); + } $data{location} = $location; $data{archived} = (defined($location) and ($location eq 'archive'))?1:0; $data{bug_num} = $param{bug}; + # mergedwith occasionally is sorted badly. Fix it to always be sorted by <=> + # and not include this bug + if (defined $data{mergedwith} and + $data{mergedwith}) { + $data{mergedwith} = + join(' ', + grep { $_ != $data{bug_num}} + sort { $a <=> $b } + split / /, $data{mergedwith} + ); + } return \%data; } @@ -299,20 +331,42 @@ our $ditch_empty = sub{ return grep {length $_} map {split $splitter} @t; }; -my $ditch_empty_space = sub {return &{$ditch_empty}(' ',@_)}; +our $sort_and_unique = sub { + my @v; + my %u; + my $all_numeric = 1; + for my $v (@_) { + if ($all_numeric and $v =~ /\D/) { + $all_numeric = 0; + } + next if exists $u{$v}; + $u{$v} = 1; + push @v, $v; + } + if ($all_numeric) { + return sort {$a <=> $b} @v; + } else { + return sort @v; + } +}; + +my $ditch_space_unique_and_sort = sub {return &{$sort_and_unique}(&{$ditch_empty}(' ',@_))}; my %split_fields = (package => \&splitpackages, affects => \&splitpackages, - blocks => $ditch_empty_space, - blockedby => $ditch_empty_space, + # Ideally we won't have to split source, but because some consumers of + # get_bug_status cannot handle arrayref, we will split it here. + source => \&splitpackages, + blocks => $ditch_space_unique_and_sort, + blockedby => $ditch_space_unique_and_sort, # this isn't strictly correct, but we'll split both of them for # the time being until we ditch all use of keywords everywhere # from the code - keywords => $ditch_empty_space, - tags => $ditch_empty_space, - found_versions => $ditch_empty_space, - fixed_versions => $ditch_empty_space, - mergedwith => $ditch_empty_space, + keywords => $ditch_space_unique_and_sort, + tags => $ditch_space_unique_and_sort, + found_versions => $ditch_space_unique_and_sort, + fixed_versions => $ditch_space_unique_and_sort, + mergedwith => $ditch_space_unique_and_sort, ); sub split_status_fields { @@ -412,7 +466,6 @@ data. =cut sub lockreadbugmerge { - my ($bug_num,$location) = @_; my $data = lockreadbug(@_); if (not defined $data) { return (0,undef); @@ -508,12 +561,17 @@ sub lock_read_all_merged_bugs { push @data,$newdata; # perform a sanity check to make sure that the merged bugs # are all merged with eachother - my $expectmerge= join(' ',grep {$_ != $bug } sort { $a <=> $b } @bugs); + # We do a cmp sort instead of an <=> sort here, because that's + # what merge does + my $expectmerge= + join(' ',grep {$_ != $bug } + sort { $a <=> $b } + @bugs); if ($newdata->{mergedwith} ne $expectmerge) { for (1..$locks) { unfilelock(exists $param{locks}?$param{locks}:()); } - die "Bug $param{bug} differs from bug $bug: ($newdata->{bug_num}: '$newdata->{mergedwith}') vs. ('$expectmerge') (".join(' ',@bugs).")"; + die "Bug $param{bug} mergedwith differs from bug $bug: ($newdata->{bug_num}: '$newdata->{mergedwith}') vs. ('$expectmerge') (".join(' ',@bugs).")"; } } } @@ -653,7 +711,7 @@ sub makestatus { Writes the bug status and summary files out. -Skips writting out a status file if minversion is 2 +Skips writing out a status file if minversion is 2 Does not call bughook if disablebughook is true. @@ -726,9 +784,9 @@ sub addfoundversions { my $version = shift; my $isbinary = shift; return unless defined $version; - undef $package if $package =~ m[(?:\s|/)]; + undef $package if defined $package and $package =~ m[(?:\s|/)]; my $source = $package; - if ($package =~ s/^src://) { + if (defined $package and $package =~ s/^src://) { $isbinary = 0; $source = $package; } @@ -771,7 +829,7 @@ exactly are removed. Otherwise, all versions matching the version number are removed. Currently $package and $isbinary are entirely ignored, but accepted -for backwards compatibilty. +for backwards compatibility. =cut @@ -907,20 +965,24 @@ Returns undef on failure. # This will eventually need to be fixed before we start using mod_perl our $version_cache = {}; sub bug_archiveable{ + state $spec = {bug => {type => SCALAR, + regex => qr/^\d+$/, + }, + status => {type => HASHREF, + optional => 1, + }, + days_until => {type => BOOLEAN, + default => 0, + }, + ignore_time => {type => BOOLEAN, + default => 0, + }, + schema => {type => OBJECT, + optional => 1, + }, + }; my %param = validate_with(params => \@_, - spec => {bug => {type => SCALAR, - regex => qr/^\d+$/, - }, - status => {type => HASHREF, - optional => 1, - }, - days_until => {type => BOOLEAN, - default => 0, - }, - ignore_time => {type => BOOLEAN, - default => 0, - }, - }, + spec => $spec, ); # This is what we return if the bug cannot be archived. my $cannot_archive = $param{days_until}?-1:0; @@ -952,16 +1014,16 @@ sub bug_archiveable{ # If we just are checking if the bug can be archived, we'll not even bother # checking the versioning information if the bug has been -done for less than 28 days. my $log_file = getbugcomponent($param{bug},'log'); - if (not defined $log_file) { + if (not defined $log_file or not -e $log_file) { print STDERR "Cannot archive $param{bug} because the log doesn't exist\n" if $DEBUG; return $cannot_archive; } - my $max_log_age = max(map {$config{remove_age} - -M $_} - $log_file, map {my $log = getbugcomponent($_,'log'); + my @log_files = $log_file, (map {my $log = getbugcomponent($_,'log'); defined $log ? ($log) : (); } - split / /, $status->{mergedwith} - ); + split / /, $status->{mergedwith}); + my $max_log_age = max(map {-e $_?($config{remove_age} - -M _):0} + @log_files); if (not $param{days_until} and not $param{ignore_time} and $max_log_age > 0 ) { @@ -1005,6 +1067,7 @@ sub bug_archiveable{ my @sourceversions = get_versions(package => $status->{package}, dist => [keys %dists], source => 1, + hash_slice(%param,'schema'), ); @source_versions{@sourceversions} = (1) x @sourceversions; # If the bug has not been fixed in the versions actually @@ -1015,6 +1078,7 @@ sub bug_archiveable{ fixed => $status->{fixed_versions}, version_cache => $version_cache, package => $status->{package}, + hash_slice(%param,'schema'), )) { print STDERR "Cannot archive $param{bug} because it's found\n" if $DEBUG; return $cannot_archive; @@ -1035,6 +1099,7 @@ sub bug_archiveable{ dist => [keys %dists], source => 1, time => 1, + hash_slice(%param,'schema'), ); for my $version (sort {$time_versions{$b} <=> $time_versions{$a}} keys %time_versions) { my $buggy = buggy(bug => $param{bug}, @@ -1043,6 +1108,7 @@ sub bug_archiveable{ fixed => $status->{fixed_versions}, version_cache => $version_cache, package => $status->{package}, + hash_slice(%param,'schema'), ); last if $buggy eq 'found'; $min_fixed_time = min($time_versions{$version},$min_fixed_time); @@ -1107,98 +1173,257 @@ a source package). Defaults to true. Note: Currently the version information is cached; this needs to be changed before using this function in long lived programs. +=head3 Returns + +Currently returns a hashref of status with the following keys. + +=over + +=item id -- bug number + +=item bug_num -- duplicate of id + +=item keywords -- tags set on the bug, including usertags if bugusertags passed. + +=item tags -- duplicate of keywords + +=item package -- name of package that the bug is assigned to + +=item severity -- severity of the bug + +=item pending -- pending state of the bug; one of following possible +values; values listed later have precedence if multiple conditions are +satisifed: + +=over + +=item pending -- default state + +=item forwarded -- bug has been forwarded + +=item pending-fixed -- bug is tagged pending + +=item fixed -- bug is tagged fixed + +=item absent -- bug does not apply to this distribution/architecture + +=item done -- bug is resolved in this distribution/architecture + +=back + +=item location -- db-h or archive; the location in the filesystem + +=item subject -- title of the bug + +=item last_modified -- epoch that the bug was last modified + +=item date -- epoch that the bug was filed + +=item originator -- bug reporter + +=item log_modified -- epoch that the log file was last modified + +=item msgid -- Message id of the original bug report + +=back + + +Other key/value pairs are returned but are not currently documented here. + =cut sub get_bug_status { if (@_ == 1) { unshift @_, 'bug'; } + state $spec = + {bug => {type => SCALAR, + regex => qr/^\d+$/, + }, + status => {type => HASHREF, + optional => 1, + }, + bug_index => {type => OBJECT, + optional => 1, + }, + version => {type => SCALAR|ARRAYREF, + optional => 1, + }, + dist => {type => SCALAR|ARRAYREF, + optional => 1, + }, + arch => {type => SCALAR|ARRAYREF, + optional => 1, + }, + bugusertags => {type => HASHREF, + optional => 1, + }, + sourceversions => {type => ARRAYREF, + optional => 1, + }, + indicatesource => {type => BOOLEAN, + default => 1, + }, + binary_to_source_cache => {type => HASHREF, + optional => 1, + }, + schema => {type => OBJECT, + optional => 1, + }, + }; my %param = validate_with(params => \@_, - spec => {bug => {type => SCALAR, - regex => qr/^\d+$/, - }, - status => {type => HASHREF, - optional => 1, - }, - bug_index => {type => OBJECT, - optional => 1, - }, - version => {type => SCALAR|ARRAYREF, - optional => 1, - }, - dist => {type => SCALAR|ARRAYREF, - optional => 1, - }, - arch => {type => SCALAR|ARRAYREF, - optional => 1, - }, - bugusertags => {type => HASHREF, - optional => 1, - }, - sourceversions => {type => ARRAYREF, - optional => 1, - }, - indicatesource => {type => BOOLEAN, - default => 1, - }, - }, + spec => $spec, ); my %status; if (defined $param{bug_index} and exists $param{bug_index}{$param{bug}}) { - %status = %{ $param{bug_index}{$param{bug}} }; - $status{pending} = $status{ status }; - $status{id} = $param{bug}; - return \%status; - } - if (defined $param{status}) { - %status = %{$param{status}}; + %status = %{ $param{bug_index}{$param{bug}} }; + $status{pending} = $status{ status }; + $status{id} = $param{bug}; + return \%status; } - else { - my $location = getbuglocation($param{bug}, 'summary'); - return {} if not defined $location or not length $location; - %status = %{ readbug( $param{bug}, $location ) }; + my $statuses = get_bug_statuses(@_); + if (exists $statuses->{$param{bug}}) { + return $statuses->{$param{bug}}; + } else { + return {}; } - $status{id} = $param{bug}; +} - if (defined $param{bugusertags}{$param{bug}}) { - $status{keywords} = "" unless defined $status{keywords}; - $status{keywords} .= " " unless $status{keywords} eq ""; - $status{keywords} .= join(" ", @{$param{bugusertags}{$param{bug}}}); +sub get_bug_statuses { + state $spec = + {bug => {type => SCALAR|ARRAYREF, + }, + status => {type => HASHREF, + optional => 1, + }, + bug_index => {type => OBJECT, + optional => 1, + }, + version => {type => SCALAR|ARRAYREF, + optional => 1, + }, + dist => {type => SCALAR|ARRAYREF, + optional => 1, + }, + arch => {type => SCALAR|ARRAYREF, + optional => 1, + }, + bugusertags => {type => HASHREF, + optional => 1, + }, + sourceversions => {type => ARRAYREF, + optional => 1, + }, + indicatesource => {type => BOOLEAN, + default => 1, + }, + binary_to_source_cache => {type => HASHREF, + optional => 1, + }, + schema => {type => OBJECT, + optional => 1, + }, + }; + my %param = validate_with(params => \@_, + spec => $spec, + ); + my $bin_to_src_cache = {}; + if (defined $param{binary_to_source_cache}) { + $bin_to_src_cache = $param{binary_to_source_cache}; } - $status{tags} = $status{keywords}; - my %tags = map { $_ => 1 } split ' ', $status{tags}; - - $status{package} = '' if not defined $status{package}; - $status{"package"} =~ s/\s*$//; - - $status{source} = binary_to_source(binary=>[split /\s*,\s*/, $status{package}], - source_only => 1, - ); - - $status{"package"} = 'unknown' if ($status{"package"} eq ''); - $status{"severity"} = 'normal' if (not defined $status{severity} or $status{"severity"} eq ''); - - $status{"pending"} = 'pending'; - $status{"pending"} = 'forwarded' if (length($status{"forwarded"})); - $status{"pending"} = 'pending-fixed' if ($tags{pending}); - $status{"pending"} = 'fixed' if ($tags{fixed}); - + my %status; + my %statuses; + if (defined $param{schema}) { + my @bug_statuses = + $param{schema}->resultset('BugStatus')-> + search_rs({id => [make_list($param{bug})]}, + {result_class => 'DBIx::Class::ResultClass::HashRefInflator'})-> + all(); + for my $bug_status (@bug_statuses) { + $statuses{$bug_status->{bug_num}} = + $bug_status; + for my $field (qw(blocks blockedby done), + qw(tags mergedwith affects) + ) { + $bug_status->{$field} //=''; + } + $bug_status->{keywords} = + $bug_status->{tags}; + $bug_status->{location} = $bug_status->{archived}?'archive':'db-h'; + for my $field (qw(found_versions fixed_versions found_date fixed_date)) { + $bug_status->{$field} = [split ' ', $bug_status->{$field} // '']; + } + for my $field (qw(found fixed)) { + # create the found/fixed hashes which indicate when a + # particular version was marked found or marked fixed. + @{$bug_status->{$field}}{@{$bug_status->{"${field}_versions"}}} = + (('') x (@{$bug_status->{"${field}_versions"}} - + @{$bug_status->{"${field}_date"}}), + @{$bug_status->{"${field}_date"}}); + } + $bug_status->{id} = $bug_status->{bug_num}; + } + } else { + for my $bug (make_list($param{bug})) { + if (defined $param{bug_index} and + exists $param{bug_index}{$bug}) { + my %status = %{$param{bug_index}{$bug}}; + $status{pending} = $status{status}; + $status{id} = $bug; + $statuses{$bug} = \%status; + } + elsif (defined $param{status} and + $param{status}{bug_num} == $bug + ) { + $statuses{$bug} = {%{$param{status}}}; + } else { + my $location = getbuglocation($bug, 'summary'); + next if not defined $location or not length $location; + my %status = %{ readbug( $bug, $location ) }; + $status{id} = $bug; + $statuses{$bug} = \%status; + } + } + } + for my $bug (keys %statuses) { + my $status = $statuses{$bug}; - my $presence = bug_presence(status => \%status, - map{(exists $param{$_})?($_,$param{$_}):()} - qw(bug sourceversions arch dist version found fixed package) - ); - if (defined $presence) { - if ($presence eq 'fixed') { - $status{pending} = 'done'; - } - elsif ($presence eq 'absent') { - $status{pending} = 'absent'; - } + if (defined $param{bugusertags}{$param{bug}}) { + $status->{keywords} = "" unless defined $status->{keywords}; + $status->{keywords} .= " " unless $status->{keywords} eq ""; + $status->{keywords} .= join(" ", @{$param{bugusertags}{$param{bug}}}); + } + $status->{tags} = $status->{keywords}; + my %tags = map { $_ => 1 } split ' ', $status->{tags}; + + $status->{package} = '' if not defined $status->{package}; + $status->{"package"} =~ s/\s*$//; + + $status->{"package"} = 'unknown' if ($status->{"package"} eq ''); + $status->{"severity"} = 'normal' if (not defined $status->{severity} or $status->{"severity"} eq ''); + + $status->{"pending"} = 'pending'; + $status->{"pending"} = 'forwarded' if (length($status->{"forwarded"})); + $status->{"pending"} = 'pending-fixed' if ($tags{pending}); + $status->{"pending"} = 'fixed' if ($tags{fixed}); + + + my $presence = bug_presence(status => $status, + bug => $bug, + map{(exists $param{$_})?($_,$param{$_}):()} + qw(sourceversions arch dist version found fixed package) + ); + if (defined $presence) { + if ($presence eq 'fixed') { + $status->{pending} = 'done'; + } elsif ($presence eq 'absent') { + $status->{pending} = 'absent'; + } + } } - return \%status; + return \%statuses; } =head2 bug_presence @@ -1420,6 +1645,9 @@ sub max_buggy{ version_cache => {type => HASHREF, default => {}, }, + schema => {type => OBJECT, + optional => 1, + }, }, ); # Resolve bugginess states (we might be looking at multiple @@ -1482,6 +1710,9 @@ sub buggy { }, version => {type => SCALAR, }, + schema => {type => OBJECT, + optional => 1, + }, }, ); my @found = @{$param{found}}; @@ -1516,8 +1747,10 @@ sub buggy { my $version_fh = IO::File->new("$config{version_packages_dir}/$srchash/$source", 'r'); if (not defined $version_fh) { # We only want to warn if it's a package which actually has a maintainer - my $maints = getmaintainers(); - next if not exists $maints->{$source}; + my @maint = package_maintainer(source => $source, + hash_slice(%param,'schema'), + ); + next unless @maint; warn "Bug $param{bug}: unable to open $config{version_packages_dir}/$srchash/$source: $!"; next; } @@ -1540,6 +1773,39 @@ sub isstrongseverity { return grep { $_ eq $severity } @{$config{strong_severities}}; } +=head1 indexdb + +=head2 generate_index_db_line + + my $data = read_bug(bug => $bug, + location => $initialdir); + # generate_index_db_line hasn't been written yet at all. + my $line = generate_index_db_line($data); + +Returns a line for a bug suitable to be written out to index.db. + +=cut + +sub generate_index_db_line { + my ($data,$bug) = @_; + + # just in case someone has given us a split out data + $data = join_status_fields($data); + + my $whendone = "open"; + my $severity = $config{default_severity}; + (my $pkglist = $data->{package}) =~ s/[,\s]+/,/g; + $pkglist =~ s/^,+//; + $pkglist =~ s/,+$//; + $whendone = "forwarded" if defined $data->{forwarded} and length $data->{forwarded}; + $whendone = "done" if defined $data->{done} and length $data->{done}; + $severity = $data->{severity} if length $data->{severity}; + return sprintf "%s %d %d %s [%s] %s %s\n", + $pkglist, $data->{bug_num}//$bug, $data->{date}, $whendone, + $data->{originator}, $severity, $data->{keywords}; +} + + =head1 PRIVATE FUNCTIONS @@ -1556,6 +1822,8 @@ sub update_realtime { my $idx_new = IO::File->new($file.'.new','w') or die "Couldn't open ${file}.new: $!"; + binmode($idx_old,':raw:utf8'); + binmode($idx_new,':raw:encoding(UTF-8)'); my $min_bug = min(keys %bugs); my $line; my @line; @@ -1620,19 +1888,7 @@ sub bughook { my $data = $bugs_temp{$bug}; appendfile("$config{spool_dir}/debbugs.trace","$type $bug\n",makestatus($data, 1)); - my $whendone = "open"; - my $severity = $config{default_severity}; - (my $pkglist = $data->{package}) =~ s/[,\s]+/,/g; - $pkglist =~ s/^,+//; - $pkglist =~ s/,+$//; - $whendone = "forwarded" if defined $data->{forwarded} and length $data->{forwarded}; - $whendone = "done" if defined $data->{done} and length $data->{done}; - $severity = $data->{severity} if length $data->{severity}; - - my $k = sprintf "%s %d %d %s [%s] %s %s\n", - $pkglist, $bug, $data->{date}, $whendone, - $data->{originator}, $severity, $data->{keywords}; - $bugs{$bug} = $k; + $bugs{$bug} = generate_index_db_line($data,$bug); } update_realtime("$config{spool_dir}/index.db.realtime", %bugs);