X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=Debbugs%2FCommon.pm;h=bb42bf8b207c171f240c48779a3c18b680124ddc;hb=df30c31db4ec21de37af9a7dd751ab86b5a83ea5;hp=a86460b0b82a8ad1ffbb42f8cf679c3012cfd1d9;hpb=be7b9935921b713b25dc1d01bea960d9eb88f13d;p=debbugs.git diff --git a/Debbugs/Common.pm b/Debbugs/Common.pm index a86460b..bb42bf8 100644 --- a/Debbugs/Common.pm +++ b/Debbugs/Common.pm @@ -1,3 +1,11 @@ +# This module is part of debbugs, and is released +# under the terms of the GPL version 2, or any later +# version at your option. +# See the file README and COPYING for more information. +# +# [Other people have contributed to this file; their copyrights should +# go here too.] +# Copyright 2007 by Don Armstrong . package Debbugs::Common; @@ -31,13 +39,17 @@ BEGIN{ @EXPORT = (); %EXPORT_TAGS = (util => [qw(getbugcomponent getbuglocation getlocationpath get_hashname), - qw(appendfile), + qw(appendfile buglog getparsedaddrs getmaintainers), + qw(getmaintainers_reverse), + qw(getpseudodesc), ], + misc => [qw(make_list)], + date => [qw(secs_to_english)], quit => [qw(quit)], - lock => [qw(filelock unfilelock)], + lock => [qw(filelock unfilelock @cleanups lockpid)], ); @EXPORT_OK = (); - Exporter::export_ok_tags(qw(read util)); + Exporter::export_ok_tags(qw(lock quit date util misc)); $EXPORT_TAGS{all} = [@EXPORT_OK]; } @@ -45,9 +57,13 @@ BEGIN{ use Debbugs::Config qw(:config); use IO::File; use Debbugs::MIME qw(decode_rfc1522); +use Mail::Address; +use Cwd qw(cwd); use Fcntl qw(:flock); +our $DEBUG_FH = \*STDERR if not defined $DEBUG_FH; + =head1 UTILITIES The following functions are exported by the C<:util> tag @@ -73,10 +89,9 @@ sub getbugcomponent { return undef if defined $location and ($location ne 'db' and $location ne 'db-h'); } - return undef if not defined $location; my $dir = getlocationpath($location); return undef if not defined $dir; - if ($location eq 'db') { + if (defined $location and $location eq 'db') { return "$dir/$bugnum.$ext"; } else { my $hash = get_hashname($bugnum); @@ -137,6 +152,22 @@ sub get_hashname { return sprintf "%02d", $_[ 0 ] % 100; } +=head2 buglog + + buglog($bugnum); + +Returns the path to the logfile corresponding to the bug. + +=cut + +sub buglog { + my $bugnum = shift; + my $location = getbuglocation($bugnum, 'log'); + return getbugcomponent($bugnum, 'log', $location) if ($location); + $location = getbuglocation($bugnum, 'log.gz'); + return getbugcomponent($bugnum, 'log.gz', $location); +} + =head2 appendfile @@ -149,14 +180,132 @@ Opens a file for appending and writes data to it. sub appendfile { my $file = shift; if (!open(AP,">>$file")) { - print DEBUG "failed open log<\n"; - print DEBUG "failed open log err $!<\n"; + print $DEBUG_FH "failed open log<\n" if $DEBUG; + print $DEBUG_FH "failed open log err $!<\n" if $DEBUG; &quit("opening $file (appendfile): $!"); } print(AP @_) || &quit("writing $file (appendfile): $!"); close(AP) || &quit("closing $file (appendfile): $!"); } +=head2 getparsedaddrs + + my $address = getparsedaddrs($address); + my @address = getparsedaddrs($address); + +Returns the output from Mail::Address->parse, or the cached output if +this address has been parsed before. In SCALAR context returns the +first address parsed. + +=cut + + +our %_parsedaddrs; +sub getparsedaddrs { + my $addr = shift; + return () unless defined $addr; + return wantarray?@{$_parsedaddrs{$addr}}:$_parsedaddrs{$addr}[0] + if exists $_parsedaddrs{$addr}; + { + # don't display the warnings from Mail::Address->parse + local $SIG{__WARN__} = sub { }; + @{$_parsedaddrs{$addr}} = Mail::Address->parse($addr); + } + return wantarray?@{$_parsedaddrs{$addr}}:$_parsedaddrs{$addr}[0]; +} + +our $_maintainer; +our $_maintainer_rev; +sub getmaintainers { + return $_maintainer if $_maintainer; + my %maintainer; + my %maintainer_rev; + for my $file (@config{qw(maintainer_file maintainer_file_override pseduo_maint_file)}) { + next unless defined $file; + my $maintfile = new IO::File $file,'r' or + &quitcgi("Unable to open $file: $!"); + while(<$maintfile>) { + next unless m/^(\S+)\s+(\S.*\S)\s*$/; + ($a,$b)=($1,$2); + $a =~ y/A-Z/a-z/; + $maintainer{$a}= $b; + for my $maint (map {lc($_->address)} getparsedaddrs($b)) { + push @{$maintainer_rev{$maint}},$a; + } + } + close($maintfile); + } + $_maintainer = \%maintainer; + $_maintainer_rev = \%maintainer_rev; + return $_maintainer; +} +sub getmaintainers_reverse{ + return $_maintainer_rev if $_maintainer_rev; + getmaintainers(); + return $_maintainer_rev; +} + +=head2 getpseudodesc + + my $pseudopkgdesc = getpseudodesc(...); + +Returns the entry for a pseudo package from the +$config{pseudo_desc_file}. In cases where pseudo_desc_file is not +defined, returns an empty arrayref. + +This function can be used to see if a particular package is a +pseudopackage or not. + +=cut + +our $_pseudodesc; +sub getpseudodesc { + return $_pseudodesc if $_pseudodesc; + my %pseudodesc; + + if (not defined $config{pseudo_desc_file}) { + $_pseudodesc = {}; + return $_pseudodesc; + } + my $pseudo = IO::File->new($config{pseudo_desc_file},'r') + or die "Unable to open $config{pseudo_desc_file}: $!"; + while(<$pseudo>) { + next unless m/^(\S+)\s+(\S.*\S)\s*$/; + $pseudodesc{lc $1} = $2; + } + close($pseudo); + $_pseudodesc = \%pseudodesc; + return $_pseudodesc; +} + + +=head1 DATE + + my $english = secs_to_english($seconds); + my ($days,$english) = secs_to_english($seconds); + +XXX This should probably be changed to use Date::Calc + +=cut + +sub secs_to_english{ + my ($seconds) = @_; + + my $days = int($seconds / 86400); + my $years = int($days / 365); + $days %= 365; + my $result; + my @age; + push @age, "1 year" if ($years == 1); + push @age, "$years years" if ($years > 1); + push @age, "1 day" if ($days == 1); + push @age, "$days days" if ($days > 1); + $result .= join(" and ", @age); + + return wantarray?(int($seconds/86400),$result):$result; +} + + =head1 LOCK These functions are exported with the :lock tag @@ -169,20 +318,24 @@ FLOCKs the passed file. Use unfilelock to unlock it. =cut -my @filelocks; +our @filelocks; +our @cleanups; sub filelock { # NB - NOT COMPATIBLE WITH `with-lock' my ($lockfile) = @_; - my ($count,$errors) = @_; + if ($lockfile !~ m{^/}) { + $lockfile = cwd().'/'.$lockfile; + } + my ($count,$errors); $count= 10; $errors= ''; for (;;) { my $fh = eval { - my $fh = new IO::File $lockfile,'w' + my $fh2 = IO::File->new($lockfile,'w') or die "Unable to open $lockfile for writing: $!"; - flock($fh,LOCK_EX|LOCK_NB) + flock($fh2,LOCK_EX|LOCK_NB) or die "Unable to lock $lockfile $!"; - return $fh; + return $fh2; }; if ($@) { $errors .= $@; @@ -224,9 +377,41 @@ sub unfilelock { close($fl{fh}) or warn "Unable to close lockfile $fl{file}: $!"; unlink($fl{file}) - or warn "Unable to unlink locfile $fl{file}: $!"; + or warn "Unable to unlink lockfile $fl{file}: $!"; } +=head2 lockpid + + lockpid('/path/to/pidfile'); + +Creates a pidfile '/path/to/pidfile' if one doesn't exist or if the +pid in the file does not respond to kill 0. + +Returns 1 on success, false on failure; dies on unusual errors. + +=cut + +sub lockpid { + my ($pidfile) = @_; + if (-e $pidfile) { + my $pidfh = IO::File->new($pidfile, 'r') or + die "Unable to open pidfile $pidfile: $!"; + local $/; + my $pid = <$pidfh>; + ($pid) = $pid =~ /(\d+)/; + if (defined $pid and kill(0,$pid)) { + return 0; + } + close $pidfh; + unlink $pidfile or + die "Unable to unlink stale pidfile $pidfile $!"; + } + my $pidfh = IO::File->new($pidfile,'w') or + die "Unable to open $pidfile for writing: $!"; + print {$pidfh} $$ or die "Unable to write to $pidfile $!"; + close $pidfh or die "Unable to close $pidfile $!"; + return 1; +} =head1 QUIT @@ -245,12 +430,31 @@ instead. (Or possibly a die handler, if the cleanups are important) =cut sub quit { - print DEBUG "quitting >$_[0]<\n"; - local ($u); + print $DEBUG_FH "quitting >$_[0]<\n" if $DEBUG; + my ($u); while ($u= $cleanups[$#cleanups]) { &$u; } die "*** $_[0]\n"; } +=head1 MISC + +These functions are exported with the :misc tag + +=head2 make_list + + LIST = make_list(@_); + +Turns a scalar or an arrayref into a list; expands a list of arrayrefs +into a list. + +That is, make_list([qw(a b c)]); returns qw(a b c); make_list([qw(a +b)],[qw(c d)] returns qw(a b c d); + +=cut + +sub make_list { + return map {(ref($_) eq 'ARRAY')?@{$_}:$_} @_; +}