Blame lib/Autom4te/Channels.pm

Packit Service 9646c7
# Copyright (C) 2002-2012 Free Software Foundation, Inc.
Packit Service 9646c7
Packit Service 9646c7
# This program is free software; you can redistribute it and/or modify
Packit Service 9646c7
# it under the terms of the GNU General Public License as published by
Packit Service 9646c7
# the Free Software Foundation; either version 2, or (at your option)
Packit Service 9646c7
# any later version.
Packit Service 9646c7
Packit Service 9646c7
# This program is distributed in the hope that it will be useful,
Packit Service 9646c7
# but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 9646c7
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service 9646c7
# GNU General Public License for more details.
Packit Service 9646c7
Packit Service 9646c7
# You should have received a copy of the GNU General Public License
Packit Service 9646c7
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
Packit Service 9646c7
Packit Service 9646c7
###############################################################
Packit Service 9646c7
# The main copy of this file is in Automake's git repository. #
Packit Service 9646c7
# Updates should be sent to automake-patches@gnu.org.         #
Packit Service 9646c7
###############################################################
Packit Service 9646c7
Packit Service 9646c7
package Autom4te::Channels;
Packit Service 9646c7
Packit Service 9646c7
=head1 NAME
Packit Service 9646c7
Packit Service 9646c7
Autom4te::Channels - support functions for error and warning management
Packit Service 9646c7
Packit Service 9646c7
=head1 SYNOPSIS
Packit Service 9646c7
Packit Service 9646c7
  use Autom4te::Channels;
Packit Service 9646c7
Packit Service 9646c7
  # Register a channel to output warnings about unused variables.
Packit Service 9646c7
  register_channel 'unused', type => 'warning';
Packit Service 9646c7
Packit Service 9646c7
  # Register a channel for system errors.
Packit Service 9646c7
  register_channel 'system', type => 'error', exit_code => 4;
Packit Service 9646c7
Packit Service 9646c7
  # Output a message on channel 'unused'.
Packit Service 9646c7
  msg 'unused', "$file:$line", "unused variable '$var'";
Packit Service 9646c7
Packit Service 9646c7
  # Make the 'unused' channel silent.
Packit Service 9646c7
  setup_channel 'unused', silent => 1;
Packit Service 9646c7
Packit Service 9646c7
  # Turn on all channels of type 'warning'.
Packit Service 9646c7
  setup_channel_type 'warning', silent => 0;
Packit Service 9646c7
Packit Service 9646c7
  # Redirect all channels to push messages on a Thread::Queue using
Packit Service 9646c7
  # the specified serialization key.
Packit Service 9646c7
  setup_channel_queue $queue, $key;
Packit Service 9646c7
Packit Service 9646c7
  # Output a message pending in a Thread::Queue.
Packit Service 9646c7
  pop_channel_queue $queue;
Packit Service 9646c7
Packit Service 9646c7
  # Treat all warnings as errors.
Packit Service 9646c7
  $warnings_are_errors = 1;
Packit Service 9646c7
Packit Service 9646c7
  # Exit with the greatest exit code encountered so far.
Packit Service 9646c7
  exit $exit_code;
Packit Service 9646c7
Packit Service 9646c7
=head1 DESCRIPTION
Packit Service 9646c7
Packit Service 9646c7
This perl module provides support functions for handling diagnostic
Packit Service 9646c7
channels in programs.  Channels can be registered to convey fatal,
Packit Service 9646c7
error, warning, or debug messages.  Each channel has various options
Packit Service 9646c7
(e.g. is the channel silent, should duplicate messages be removed,
Packit Service 9646c7
etc.) that can also be overridden on a per-message basis.
Packit Service 9646c7
Packit Service 9646c7
=cut
Packit Service 9646c7
Packit Service 9646c7
use 5.006;
Packit Service 9646c7
use strict;
Packit Service 9646c7
use Exporter;
Packit Service 9646c7
use Carp;
Packit Service 9646c7
use File::Basename;
Packit Service 9646c7
Packit Service 9646c7
use vars qw (@ISA @EXPORT %channels $me);
Packit Service 9646c7
Packit Service 9646c7
@ISA = qw (Exporter);
Packit Service 9646c7
@EXPORT = qw ($exit_code $warnings_are_errors
Packit Service 9646c7
	      &reset_local_duplicates &reset_global_duplicates
Packit Service 9646c7
	      &register_channel &msg &exists_channel &channel_type
Packit Service 9646c7
	      &setup_channel &setup_channel_type
Packit Service 9646c7
	      &dup_channel_setup &drop_channel_setup
Packit Service 9646c7
	      &buffer_messages &flush_messages
Packit Service 9646c7
	      &setup_channel_queue &pop_channel_queue
Packit Service 9646c7
	      US_GLOBAL US_LOCAL
Packit Service 9646c7
	      UP_NONE UP_TEXT UP_LOC_TEXT);
Packit Service 9646c7
Packit Service 9646c7
$me = basename $0;
Packit Service 9646c7
Packit Service 9646c7
=head2 Global Variables
Packit Service 9646c7
Packit Service 9646c7
=over 4
Packit Service 9646c7
Packit Service 9646c7
=item C<$exit_code>
Packit Service 9646c7
Packit Service 9646c7
The greatest exit code seen so far. C<$exit_code> is updated from
Packit Service 9646c7
the C<exit_code> options of C<fatal> and C<error> channels.
Packit Service 9646c7
Packit Service 9646c7
=cut
Packit Service 9646c7
Packit Service 9646c7
use vars qw ($exit_code);
Packit Service 9646c7
$exit_code = 0;
Packit Service 9646c7
Packit Service 9646c7
=item C<$warnings_are_errors>
Packit Service 9646c7
Packit Service 9646c7
Set this variable to 1 if warning messages should be treated as
Packit Service 9646c7
errors (i.e. if they should update C<$exit_code>).
Packit Service 9646c7
Packit Service 9646c7
=cut
Packit Service 9646c7
Packit Service 9646c7
use vars qw ($warnings_are_errors);
Packit Service 9646c7
$warnings_are_errors = 0;
Packit Service 9646c7
Packit Service 9646c7
=back
Packit Service 9646c7
Packit Service 9646c7
=head2 Constants
Packit Service 9646c7
Packit Service 9646c7
=over 4
Packit Service 9646c7
Packit Service 9646c7
=item C<UP_NONE>, C<UP_TEXT>, C<UP_LOC_TEXT>
Packit Service 9646c7
Packit Service 9646c7
Possible values for the C<uniq_part> options.  This selects the part
Packit Service 9646c7
of the message that should be considered when filtering out duplicates.
Packit Service 9646c7
If C<UP_LOC_TEXT> is used, the location and the explanation message
Packit Service 9646c7
are used for filtering.  If C<UP_TEXT> is used, only the explanation
Packit Service 9646c7
message is used (so the same message will be filtered out if it appears
Packit Service 9646c7
at different locations).  C<UP_NONE> means that duplicate messages
Packit Service 9646c7
should be output.
Packit Service 9646c7
Packit Service 9646c7
=cut
Packit Service 9646c7
Packit Service 9646c7
use constant UP_NONE => 0;
Packit Service 9646c7
use constant UP_TEXT => 1;
Packit Service 9646c7
use constant UP_LOC_TEXT => 2;
Packit Service 9646c7
Packit Service 9646c7
=item C<US_LOCAL>, C<US_GLOBAL>
Packit Service 9646c7
Packit Service 9646c7
Possible values for the C<uniq_scope> options.
Packit Service 9646c7
Use C<US_GLOBAL> for error messages that should be printed only
Packit Service 9646c7
once during the execution of the program, C<US_LOCAL> for message that
Packit Service 9646c7
should be printed only once per file.  (Actually, C<Channels> does not
Packit Service 9646c7
do this now when files are changed, it relies on you calling
Packit Service 9646c7
C<reset_local_duplicates> when this happens.)
Packit Service 9646c7
Packit Service 9646c7
=cut
Packit Service 9646c7
Packit Service 9646c7
# possible values for uniq_scope
Packit Service 9646c7
use constant US_LOCAL => 0;
Packit Service 9646c7
use constant US_GLOBAL => 1;
Packit Service 9646c7
Packit Service 9646c7
=back
Packit Service 9646c7
Packit Service 9646c7
=head2 Options
Packit Service 9646c7
Packit Service 9646c7
Channels accept the options described below.  These options can be
Packit Service 9646c7
passed as a hash to the C<register_channel>, C<setup_channel>, and C<msg>
Packit Service 9646c7
functions.  The possible keys, with their default value are:
Packit Service 9646c7
Packit Service 9646c7
=over
Packit Service 9646c7
Packit Service 9646c7
=item C<type =E<gt> 'warning'>
Packit Service 9646c7
Packit Service 9646c7
The type of the channel.  One of C<'debug'>, C<'warning'>, C<'error'>, or
Packit Service 9646c7
C<'fatal'>.  Fatal messages abort the program when they are output.
Packit Service 9646c7
Error messages update the exit status.  Debug and warning messages are
Packit Service 9646c7
harmless, except that warnings are treated as errors if
Packit Service 9646c7
C<$warnings_are_errors> is set.
Packit Service 9646c7
Packit Service 9646c7
=item C<exit_code =E<gt> 1>
Packit Service 9646c7
Packit Service 9646c7
The value to update C<$exit_code> with when a fatal or error message
Packit Service 9646c7
is emitted.  C<$exit_code> is also updated for warnings output
Packit Service 9646c7
when C<$warnings_are_errors> is set.
Packit Service 9646c7
Packit Service 9646c7
=item C<file =E<gt> \*STDERR>
Packit Service 9646c7
Packit Service 9646c7
The file where the error should be output.
Packit Service 9646c7
Packit Service 9646c7
=item C<silent =E<gt> 0>
Packit Service 9646c7
Packit Service 9646c7
Whether the channel should be silent.  Use this do disable a
Packit Service 9646c7
category of warning, for instance.
Packit Service 9646c7
Packit Service 9646c7
=item C<ordered =E<gt> 1>
Packit Service 9646c7
Packit Service 9646c7
Whether, with multi-threaded execution, the message should be queued
Packit Service 9646c7
for ordered output.
Packit Service 9646c7
Packit Service 9646c7
=item C<uniq_part =E<gt> UP_LOC_TEXT>
Packit Service 9646c7
Packit Service 9646c7
The part of the message subject to duplicate filtering.  See the
Packit Service 9646c7
documentation for the C<UP_NONE>, C<UP_TEXT>, and C<UP_LOC_TEXT>
Packit Service 9646c7
constants above.
Packit Service 9646c7
Packit Service 9646c7
C<uniq_part> can also be set to an arbitrary string that will be used
Packit Service 9646c7
instead of the message when considering duplicates.
Packit Service 9646c7
Packit Service 9646c7
=item C<uniq_scope =E<gt> US_LOCAL>
Packit Service 9646c7
Packit Service 9646c7
The scope of duplicate filtering.  See the documentation for the
Packit Service 9646c7
C<US_LOCAL>, and C<US_GLOBAL> constants above.
Packit Service 9646c7
Packit Service 9646c7
=item C<header =E<gt> ''>
Packit Service 9646c7
Packit Service 9646c7
A string to prepend to each message emitted through this channel.
Packit Service 9646c7
With partial messages, only the first part will have C<header>
Packit Service 9646c7
prepended.
Packit Service 9646c7
Packit Service 9646c7
=item C<footer =E<gt> ''>
Packit Service 9646c7
Packit Service 9646c7
A string to append to each message emitted through this channel.
Packit Service 9646c7
With partial messages, only the final part will have C<footer>
Packit Service 9646c7
appended.
Packit Service 9646c7
Packit Service 9646c7
=item C<backtrace =E<gt> 0>
Packit Service 9646c7
Packit Service 9646c7
Die with a stack backtrace after displaying the message.
Packit Service 9646c7
Packit Service 9646c7
=item C<partial =E<gt> 0>
Packit Service 9646c7
Packit Service 9646c7
When set, indicates a partial message that should
Packit Service 9646c7
be output along with the next message with C<partial> unset.
Packit Service 9646c7
Several partial messages can be stacked this way.
Packit Service 9646c7
Packit Service 9646c7
Duplicate filtering will apply to the I<global> message resulting from
Packit Service 9646c7
all I<partial> messages, using the options from the last (non-partial)
Packit Service 9646c7
message.  Linking associated messages is the main reason to use this
Packit Service 9646c7
option.
Packit Service 9646c7
Packit Service 9646c7
For instance the following messages
Packit Service 9646c7
Packit Service 9646c7
  msg 'channel', 'foo:2', 'redefinition of A ...';
Packit Service 9646c7
  msg 'channel', 'foo:1', '... A previously defined here';
Packit Service 9646c7
  msg 'channel', 'foo:3', 'redefinition of A ...';
Packit Service 9646c7
  msg 'channel', 'foo:1', '... A previously defined here';
Packit Service 9646c7
Packit Service 9646c7
will result in
Packit Service 9646c7
Packit Service 9646c7
 foo:2: redefinition of A ...
Packit Service 9646c7
 foo:1: ... A previously defined here
Packit Service 9646c7
 foo:3: redefinition of A ...
Packit Service 9646c7
Packit Service 9646c7
where the duplicate "I<... A previously defined here>" has been
Packit Service 9646c7
filtered out.
Packit Service 9646c7
Packit Service 9646c7
Linking these messages using C<partial> as follows will prevent the
Packit Service 9646c7
fourth message to disappear.
Packit Service 9646c7
Packit Service 9646c7
  msg 'channel', 'foo:2', 'redefinition of A ...', partial => 1;
Packit Service 9646c7
  msg 'channel', 'foo:1', '... A previously defined here';
Packit Service 9646c7
  msg 'channel', 'foo:3', 'redefinition of A ...', partial => 1;
Packit Service 9646c7
  msg 'channel', 'foo:1', '... A previously defined here';
Packit Service 9646c7
Packit Service 9646c7
Note that because the stack of C<partial> messages is printed with the
Packit Service 9646c7
first non-C<partial> message, most options of C<partial> messages will
Packit Service 9646c7
be ignored.
Packit Service 9646c7
Packit Service 9646c7
=back
Packit Service 9646c7
Packit Service 9646c7
=cut
Packit Service 9646c7
Packit Service 9646c7
use vars qw (%_default_options %_global_duplicate_messages
Packit Service 9646c7
	     %_local_duplicate_messages);
Packit Service 9646c7
Packit Service 9646c7
# Default options for a channel.
Packit Service 9646c7
%_default_options =
Packit Service 9646c7
  (
Packit Service 9646c7
   type => 'warning',
Packit Service 9646c7
   exit_code => 1,
Packit Service 9646c7
   file => \*STDERR,
Packit Service 9646c7
   silent => 0,
Packit Service 9646c7
   ordered => 1,
Packit Service 9646c7
   queue => 0,
Packit Service 9646c7
   queue_key => undef,
Packit Service 9646c7
   uniq_scope => US_LOCAL,
Packit Service 9646c7
   uniq_part => UP_LOC_TEXT,
Packit Service 9646c7
   header => '',
Packit Service 9646c7
   footer => '',
Packit Service 9646c7
   backtrace => 0,
Packit Service 9646c7
   partial => 0,
Packit Service 9646c7
   );
Packit Service 9646c7
Packit Service 9646c7
# Filled with output messages as keys, to detect duplicates.
Packit Service 9646c7
# The value associated with each key is the number of occurrences
Packit Service 9646c7
# filtered out.
Packit Service 9646c7
%_local_duplicate_messages = ();
Packit Service 9646c7
%_global_duplicate_messages = ();
Packit Service 9646c7
Packit Service 9646c7
sub _reset_duplicates (\%)
Packit Service 9646c7
{
Packit Service 9646c7
  my ($ref) = @_;
Packit Service 9646c7
  my $dup = 0;
Packit Service 9646c7
  foreach my $k (keys %$ref)
Packit Service 9646c7
    {
Packit Service 9646c7
      $dup += $ref->{$k};
Packit Service 9646c7
    }
Packit Service 9646c7
  %$ref = ();
Packit Service 9646c7
  return $dup;
Packit Service 9646c7
}
Packit Service 9646c7
Packit Service 9646c7
Packit Service 9646c7
=head2 Functions
Packit Service 9646c7
Packit Service 9646c7
=over 4
Packit Service 9646c7
Packit Service 9646c7
=item C<reset_local_duplicates ()>
Packit Service 9646c7
Packit Service 9646c7
Reset local duplicate messages (see C<US_LOCAL>), and
Packit Service 9646c7
return the number of messages that have been filtered out.
Packit Service 9646c7
Packit Service 9646c7
=cut
Packit Service 9646c7
Packit Service 9646c7
sub reset_local_duplicates ()
Packit Service 9646c7
{
Packit Service 9646c7
  return _reset_duplicates %_local_duplicate_messages;
Packit Service 9646c7
}
Packit Service 9646c7
Packit Service 9646c7
=item C<reset_global_duplicates ()>
Packit Service 9646c7
Packit Service 9646c7
Reset local duplicate messages (see C<US_GLOBAL>), and
Packit Service 9646c7
return the number of messages that have been filtered out.
Packit Service 9646c7
Packit Service 9646c7
=cut
Packit Service 9646c7
Packit Service 9646c7
sub reset_global_duplicates ()
Packit Service 9646c7
{
Packit Service 9646c7
  return _reset_duplicates %_global_duplicate_messages;
Packit Service 9646c7
}
Packit Service 9646c7
Packit Service 9646c7
sub _merge_options (\%%)
Packit Service 9646c7
{
Packit Service 9646c7
  my ($hash, %options) = @_;
Packit Service 9646c7
  local $_;
Packit Service 9646c7
Packit Service 9646c7
  foreach (keys %options)
Packit Service 9646c7
    {
Packit Service 9646c7
      if (exists $hash->{$_})
Packit Service 9646c7
	{
Packit Service 9646c7
	  $hash->{$_} = $options{$_}
Packit Service 9646c7
	}
Packit Service 9646c7
      else
Packit Service 9646c7
	{
Packit Service 9646c7
	  confess "unknown option '$_'";
Packit Service 9646c7
	}
Packit Service 9646c7
    }
Packit Service 9646c7
  if ($hash->{'ordered'})
Packit Service 9646c7
    {
Packit Service 9646c7
      confess "fatal messages cannot be ordered"
Packit Service 9646c7
	if $hash->{'type'} eq 'fatal';
Packit Service 9646c7
      confess "backtrace cannot be output on ordered messages"
Packit Service 9646c7
	if $hash->{'backtrace'};
Packit Service 9646c7
    }
Packit Service 9646c7
}
Packit Service 9646c7
Packit Service 9646c7
=item C<register_channel ($name, [%options])>
Packit Service 9646c7
Packit Service 9646c7
Declare channel C<$name>, and override the default options
Packit Service 9646c7
with those listed in C<%options>.
Packit Service 9646c7
Packit Service 9646c7
=cut
Packit Service 9646c7
Packit Service 9646c7
sub register_channel ($;%)
Packit Service 9646c7
{
Packit Service 9646c7
  my ($name, %options) = @_;
Packit Service 9646c7
  my %channel_opts = %_default_options;
Packit Service 9646c7
  _merge_options %channel_opts, %options;
Packit Service 9646c7
  $channels{$name} = \%channel_opts;
Packit Service 9646c7
}
Packit Service 9646c7
Packit Service 9646c7
=item C<exists_channel ($name)>
Packit Service 9646c7
Packit Service 9646c7
Returns true iff channel C<$name> has been registered.
Packit Service 9646c7
Packit Service 9646c7
=cut
Packit Service 9646c7
Packit Service 9646c7
sub exists_channel ($)
Packit Service 9646c7
{
Packit Service 9646c7
  my ($name) = @_;
Packit Service 9646c7
  return exists $channels{$name};
Packit Service 9646c7
}
Packit Service 9646c7
Packit Service 9646c7
=item C<channel_type ($name)>
Packit Service 9646c7
Packit Service 9646c7
Returns the type of channel C<$name> if it has been registered.
Packit Service 9646c7
Returns the empty string otherwise.
Packit Service 9646c7
Packit Service 9646c7
=cut
Packit Service 9646c7
Packit Service 9646c7
sub channel_type ($)
Packit Service 9646c7
{
Packit Service 9646c7
  my ($name) = @_;
Packit Service 9646c7
  return $channels{$name}{'type'} if exists_channel $name;
Packit Service 9646c7
  return '';
Packit Service 9646c7
}
Packit Service 9646c7
Packit Service 9646c7
# _format_sub_message ($LEADER, $MESSAGE)
Packit Service 9646c7
# ---------------------------------------
Packit Service 9646c7
# Split $MESSAGE at new lines and add $LEADER to each line.
Packit Service 9646c7
sub _format_sub_message ($$)
Packit Service 9646c7
{
Packit Service 9646c7
  my ($leader, $message) = @_;
Packit Service 9646c7
  return $leader . join ("\n" . $leader, split ("\n", $message)) . "\n";
Packit Service 9646c7
}
Packit Service 9646c7
Packit Service 9646c7
# Store partial messages here. (See the 'partial' option.)
Packit Service 9646c7
use vars qw ($partial);
Packit Service 9646c7
$partial = '';
Packit Service 9646c7
Packit Service 9646c7
# _format_message ($LOCATION, $MESSAGE, %OPTIONS)
Packit Service 9646c7
# -----------------------------------------------
Packit Service 9646c7
# Format the message.  Return a string ready to print.
Packit Service 9646c7
sub _format_message ($$%)
Packit Service 9646c7
{
Packit Service 9646c7
  my ($location, $message, %opts) = @_;
Packit Service 9646c7
  my $msg = ($partial eq '' ? $opts{'header'} : '') . $message
Packit Service 9646c7
	    . ($opts{'partial'} ? '' : $opts{'footer'});
Packit Service 9646c7
  if (ref $location)
Packit Service 9646c7
    {
Packit Service 9646c7
      # If $LOCATION is a reference, assume it's an instance of the
Packit Service 9646c7
      # Autom4te::Location class and display contexts.
Packit Service 9646c7
      my $loc = $location->get || $me;
Packit Service 9646c7
      $msg = _format_sub_message ("$loc: ", $msg);
Packit Service 9646c7
      for my $pair ($location->get_contexts)
Packit Service 9646c7
	{
Packit Service 9646c7
	  $msg .= _format_sub_message ($pair->[0] . ":   ", $pair->[1]);
Packit Service 9646c7
	}
Packit Service 9646c7
    }
Packit Service 9646c7
  else
Packit Service 9646c7
    {
Packit Service 9646c7
      $location ||= $me;
Packit Service 9646c7
      $msg = _format_sub_message ("$location: ", $msg);
Packit Service 9646c7
    }
Packit Service 9646c7
  return $msg;
Packit Service 9646c7
}
Packit Service 9646c7
Packit Service 9646c7
# _enqueue ($QUEUE, $KEY, $UNIQ_SCOPE, $TO_FILTER, $MSG, $FILE)
Packit Service 9646c7
# -------------------------------------------------------------
Packit Service 9646c7
# Push message on a queue, to be processed by another thread.
Packit Service 9646c7
sub _enqueue ($$$$$$)
Packit Service 9646c7
{
Packit Service 9646c7
  my ($queue, $key, $uniq_scope, $to_filter, $msg, $file) = @_;
Packit Service 9646c7
  $queue->enqueue ($key, $msg, $to_filter, $uniq_scope);
Packit Service 9646c7
  confess "message queuing works only for STDERR"
Packit Service 9646c7
    if $file ne \*STDERR;
Packit Service 9646c7
}
Packit Service 9646c7
Packit Service 9646c7
# _dequeue ($QUEUE)
Packit Service 9646c7
# -----------------
Packit Service 9646c7
# Pop a message from a queue, and print, similarly to how
Packit Service 9646c7
# _print_message would do it.  Return 0 if the queue is
Packit Service 9646c7
# empty.  Note that the key has already been dequeued.
Packit Service 9646c7
sub _dequeue ($)
Packit Service 9646c7
{
Packit Service 9646c7
  my ($queue) = @_;
Packit Service 9646c7
  my $msg = $queue->dequeue || return 0;
Packit Service 9646c7
  my $to_filter = $queue->dequeue;
Packit Service 9646c7
  my $uniq_scope = $queue->dequeue;
Packit Service 9646c7
  my $file = \*STDERR;
Packit Service 9646c7
Packit Service 9646c7
  if ($to_filter ne '')
Packit Service 9646c7
    {
Packit Service 9646c7
      # Do we want local or global uniqueness?
Packit Service 9646c7
      my $dups;
Packit Service 9646c7
      if ($uniq_scope == US_LOCAL)
Packit Service 9646c7
	{
Packit Service 9646c7
	  $dups = \%_local_duplicate_messages;
Packit Service 9646c7
	}
Packit Service 9646c7
      elsif ($uniq_scope == US_GLOBAL)
Packit Service 9646c7
	{
Packit Service 9646c7
	  $dups = \%_global_duplicate_messages;
Packit Service 9646c7
	}
Packit Service 9646c7
      else
Packit Service 9646c7
	{
Packit Service 9646c7
	  confess "unknown value for uniq_scope: " . $uniq_scope;
Packit Service 9646c7
	}
Packit Service 9646c7
Packit Service 9646c7
      # Update the hash of messages.
Packit Service 9646c7
      if (exists $dups->{$to_filter})
Packit Service 9646c7
	{
Packit Service 9646c7
	  ++$dups->{$to_filter};
Packit Service 9646c7
	  return 1;
Packit Service 9646c7
	}
Packit Service 9646c7
      else
Packit Service 9646c7
	{
Packit Service 9646c7
	  $dups->{$to_filter} = 0;
Packit Service 9646c7
	}
Packit Service 9646c7
    }
Packit Service 9646c7
  print $file $msg;
Packit Service 9646c7
  return 1;
Packit Service 9646c7
}
Packit Service 9646c7
Packit Service 9646c7
Packit Service 9646c7
# _print_message ($LOCATION, $MESSAGE, %OPTIONS)
Packit Service 9646c7
# ----------------------------------------------
Packit Service 9646c7
# Format the message, check duplicates, and print it.
Packit Service 9646c7
sub _print_message ($$%)
Packit Service 9646c7
{
Packit Service 9646c7
  my ($location, $message, %opts) = @_;
Packit Service 9646c7
Packit Service 9646c7
  return 0 if ($opts{'silent'});
Packit Service 9646c7
Packit Service 9646c7
  my $msg = _format_message ($location, $message, %opts);
Packit Service 9646c7
  if ($opts{'partial'})
Packit Service 9646c7
    {
Packit Service 9646c7
      # Incomplete message.  Store, don't print.
Packit Service 9646c7
      $partial .= $msg;
Packit Service 9646c7
      return;
Packit Service 9646c7
    }
Packit Service 9646c7
  else
Packit Service 9646c7
    {
Packit Service 9646c7
      # Prefix with any partial message send so far.
Packit Service 9646c7
      $msg = $partial . $msg;
Packit Service 9646c7
      $partial = '';
Packit Service 9646c7
    }
Packit Service 9646c7
Packit Service 9646c7
  msg ('note', '', 'warnings are treated as errors', uniq_scope => US_GLOBAL)
Packit Service 9646c7
    if ($opts{'type'} eq 'warning' && $warnings_are_errors);
Packit Service 9646c7
Packit Service 9646c7
  # Check for duplicate message if requested.
Packit Service 9646c7
  my $to_filter;
Packit Service 9646c7
  if ($opts{'uniq_part'} ne UP_NONE)
Packit Service 9646c7
    {
Packit Service 9646c7
      # Which part of the error should we match?
Packit Service 9646c7
      if ($opts{'uniq_part'} eq UP_TEXT)
Packit Service 9646c7
	{
Packit Service 9646c7
	  $to_filter = $message;
Packit Service 9646c7
	}
Packit Service 9646c7
      elsif ($opts{'uniq_part'} eq UP_LOC_TEXT)
Packit Service 9646c7
	{
Packit Service 9646c7
	  $to_filter = $msg;
Packit Service 9646c7
	}
Packit Service 9646c7
      else
Packit Service 9646c7
	{
Packit Service 9646c7
	  $to_filter = $opts{'uniq_part'};
Packit Service 9646c7
	}
Packit Service 9646c7
Packit Service 9646c7
      # Do we want local or global uniqueness?
Packit Service 9646c7
      my $dups;
Packit Service 9646c7
      if ($opts{'uniq_scope'} == US_LOCAL)
Packit Service 9646c7
	{
Packit Service 9646c7
	  $dups = \%_local_duplicate_messages;
Packit Service 9646c7
	}
Packit Service 9646c7
      elsif ($opts{'uniq_scope'} == US_GLOBAL)
Packit Service 9646c7
	{
Packit Service 9646c7
	  $dups = \%_global_duplicate_messages;
Packit Service 9646c7
	}
Packit Service 9646c7
      else
Packit Service 9646c7
	{
Packit Service 9646c7
	  confess "unknown value for uniq_scope: " . $opts{'uniq_scope'};
Packit Service 9646c7
	}
Packit Service 9646c7
Packit Service 9646c7
      # Update the hash of messages.
Packit Service 9646c7
      if (exists $dups->{$to_filter})
Packit Service 9646c7
	{
Packit Service 9646c7
	  ++$dups->{$to_filter};
Packit Service 9646c7
	  return 0;
Packit Service 9646c7
	}
Packit Service 9646c7
      else
Packit Service 9646c7
	{
Packit Service 9646c7
	  $dups->{$to_filter} = 0;
Packit Service 9646c7
	}
Packit Service 9646c7
    }
Packit Service 9646c7
  my $file = $opts{'file'};
Packit Service 9646c7
  if ($opts{'ordered'} && $opts{'queue'})
Packit Service 9646c7
    {
Packit Service 9646c7
      _enqueue ($opts{'queue'}, $opts{'queue_key'}, $opts{'uniq_scope'},
Packit Service 9646c7
		$to_filter, $msg, $file);
Packit Service 9646c7
    }
Packit Service 9646c7
  else
Packit Service 9646c7
    {
Packit Service 9646c7
      print $file $msg;
Packit Service 9646c7
    }
Packit Service 9646c7
  return 1;
Packit Service 9646c7
}
Packit Service 9646c7
Packit Service 9646c7
=item C<msg ($channel, $location, $message, [%options])>
Packit Service 9646c7
Packit Service 9646c7
Emit a message on C<$channel>, overriding some options of the channel with
Packit Service 9646c7
those specified in C<%options>.  Obviously C<$channel> must have been
Packit Service 9646c7
registered with C<register_channel>.
Packit Service 9646c7
Packit Service 9646c7
C<$message> is the text of the message, and C<$location> is a location
Packit Service 9646c7
associated to the message.
Packit Service 9646c7
Packit Service 9646c7
For instance to complain about some unused variable C<mumble>
Packit Service 9646c7
declared at line 10 in F<foo.c>, one could do:
Packit Service 9646c7
Packit Service 9646c7
  msg 'unused', 'foo.c:10', "unused variable 'mumble'";
Packit Service 9646c7
Packit Service 9646c7
If channel C<unused> is not silent (and if this message is not a duplicate),
Packit Service 9646c7
the following would be output:
Packit Service 9646c7
Packit Service 9646c7
  foo.c:10: unused variable 'mumble'
Packit Service 9646c7
Packit Service 9646c7
C<$location> can also be an instance of C<Autom4te::Location>.  In this
Packit Service 9646c7
case, the stack of contexts will be displayed in addition.
Packit Service 9646c7
Packit Service 9646c7
If C<$message> contains newline characters, C<$location> is prepended
Packit Service 9646c7
to each line.  For instance,
Packit Service 9646c7
Packit Service 9646c7
  msg 'error', 'somewhere', "1st line\n2nd line";
Packit Service 9646c7
Packit Service 9646c7
becomes
Packit Service 9646c7
Packit Service 9646c7
  somewhere: 1st line
Packit Service 9646c7
  somewhere: 2nd line
Packit Service 9646c7
Packit Service 9646c7
If C<$location> is an empty string, it is replaced by the name of the
Packit Service 9646c7
program.  Actually, if you don't use C<%options>, you can even
Packit Service 9646c7
elide the empty C<$location>.  Thus
Packit Service 9646c7
Packit Service 9646c7
  msg 'fatal', '', 'fatal error';
Packit Service 9646c7
  msg 'fatal', 'fatal error';
Packit Service 9646c7
Packit Service 9646c7
both print
Packit Service 9646c7
Packit Service 9646c7
  progname: fatal error
Packit Service 9646c7
Packit Service 9646c7
=cut
Packit Service 9646c7
Packit Service 9646c7
Packit Service 9646c7
use vars qw (@backlog %buffering);
Packit Service 9646c7
Packit Service 9646c7
# See buffer_messages() and flush_messages() below.
Packit Service 9646c7
%buffering = ();	# The map of channel types to buffer.
Packit Service 9646c7
@backlog = ();		# The buffer of messages.
Packit Service 9646c7
Packit Service 9646c7
sub msg ($$;$%)
Packit Service 9646c7
{
Packit Service 9646c7
  my ($channel, $location, $message, %options) = @_;
Packit Service 9646c7
Packit Service 9646c7
  if (! defined $message)
Packit Service 9646c7
    {
Packit Service 9646c7
      $message = $location;
Packit Service 9646c7
      $location = '';
Packit Service 9646c7
    }
Packit Service 9646c7
Packit Service 9646c7
  confess "unknown channel $channel" unless exists $channels{$channel};
Packit Service 9646c7
Packit Service 9646c7
  my %opts = %{$channels{$channel}};
Packit Service 9646c7
  _merge_options (%opts, %options);
Packit Service 9646c7
Packit Service 9646c7
  if (exists $buffering{$opts{'type'}})
Packit Service 9646c7
    {
Packit Service 9646c7
      push @backlog, [$channel, $location->clone, $message, %options];
Packit Service 9646c7
      return;
Packit Service 9646c7
    }
Packit Service 9646c7
Packit Service 9646c7
  # Print the message if needed.
Packit Service 9646c7
  if (_print_message ($location, $message, %opts))
Packit Service 9646c7
    {
Packit Service 9646c7
      # Adjust exit status.
Packit Service 9646c7
      if ($opts{'type'} eq 'error'
Packit Service 9646c7
	  || $opts{'type'} eq 'fatal'
Packit Service 9646c7
	  || ($opts{'type'} eq 'warning' && $warnings_are_errors))
Packit Service 9646c7
	{
Packit Service 9646c7
	  my $es = $opts{'exit_code'};
Packit Service 9646c7
	  $exit_code = $es if $es > $exit_code;
Packit Service 9646c7
	}
Packit Service 9646c7
Packit Service 9646c7
      # Die on fatal messages.
Packit Service 9646c7
      confess if $opts{'backtrace'};
Packit Service 9646c7
      if ($opts{'type'} eq 'fatal')
Packit Service 9646c7
        {
Packit Service 9646c7
	  # flush messages explicitly here, needed in worker threads.
Packit Service 9646c7
	  STDERR->flush;
Packit Service 9646c7
	  exit $exit_code;
Packit Service 9646c7
	}
Packit Service 9646c7
    }
Packit Service 9646c7
}
Packit Service 9646c7
Packit Service 9646c7
Packit Service 9646c7
=item C<setup_channel ($channel, %options)>
Packit Service 9646c7
Packit Service 9646c7
Override the options of C<$channel> with those specified by C<%options>.
Packit Service 9646c7
Packit Service 9646c7
=cut
Packit Service 9646c7
Packit Service 9646c7
sub setup_channel ($%)
Packit Service 9646c7
{
Packit Service 9646c7
  my ($name, %opts) = @_;
Packit Service 9646c7
  confess "unknown channel $name" unless exists $channels{$name};
Packit Service 9646c7
  _merge_options %{$channels{$name}}, %opts;
Packit Service 9646c7
}
Packit Service 9646c7
Packit Service 9646c7
=item C<setup_channel_type ($type, %options)>
Packit Service 9646c7
Packit Service 9646c7
Override the options of any channel of type C<$type>
Packit Service 9646c7
with those specified by C<%options>.
Packit Service 9646c7
Packit Service 9646c7
=cut
Packit Service 9646c7
Packit Service 9646c7
sub setup_channel_type ($%)
Packit Service 9646c7
{
Packit Service 9646c7
  my ($type, %opts) = @_;
Packit Service 9646c7
  foreach my $channel (keys %channels)
Packit Service 9646c7
    {
Packit Service 9646c7
      setup_channel $channel, %opts
Packit Service 9646c7
	if $channels{$channel}{'type'} eq $type;
Packit Service 9646c7
    }
Packit Service 9646c7
}
Packit Service 9646c7
Packit Service 9646c7
=item C<dup_channel_setup ()>, C<drop_channel_setup ()>
Packit Service 9646c7
Packit Service 9646c7
Sometimes it is necessary to make temporary modifications to channels.
Packit Service 9646c7
For instance one may want to disable a warning while processing a
Packit Service 9646c7
particular file, and then restore the initial setup.  These two
Packit Service 9646c7
functions make it easy: C<dup_channel_setup ()> saves a copy of the
Packit Service 9646c7
current configuration for later restoration by
Packit Service 9646c7
C<drop_channel_setup ()>.
Packit Service 9646c7
Packit Service 9646c7
You can think of this as a stack of configurations whose first entry
Packit Service 9646c7
is the active one.  C<dup_channel_setup ()> duplicates the first
Packit Service 9646c7
entry, while C<drop_channel_setup ()> just deletes it.
Packit Service 9646c7
Packit Service 9646c7
=cut
Packit Service 9646c7
Packit Service 9646c7
use vars qw (@_saved_channels @_saved_werrors);
Packit Service 9646c7
@_saved_channels = ();
Packit Service 9646c7
@_saved_werrors = ();
Packit Service 9646c7
Packit Service 9646c7
sub dup_channel_setup ()
Packit Service 9646c7
{
Packit Service 9646c7
  my %channels_copy;
Packit Service 9646c7
  foreach my $k1 (keys %channels)
Packit Service 9646c7
    {
Packit Service 9646c7
      $channels_copy{$k1} = {%{$channels{$k1}}};
Packit Service 9646c7
    }
Packit Service 9646c7
  push @_saved_channels, \%channels_copy;
Packit Service 9646c7
  push @_saved_werrors, $warnings_are_errors;
Packit Service 9646c7
}
Packit Service 9646c7
Packit Service 9646c7
sub drop_channel_setup ()
Packit Service 9646c7
{
Packit Service 9646c7
  my $saved = pop @_saved_channels;
Packit Service 9646c7
  %channels = %$saved;
Packit Service 9646c7
  $warnings_are_errors = pop @_saved_werrors;
Packit Service 9646c7
}
Packit Service 9646c7
Packit Service 9646c7
=item C<buffer_messages (@types)>, C<flush_messages ()>
Packit Service 9646c7
Packit Service 9646c7
By default, when C<msg> is called, messages are processed immediately.
Packit Service 9646c7
Packit Service 9646c7
Sometimes it is necessary to delay the output of messages.
Packit Service 9646c7
For instance you might want to make diagnostics before
Packit Service 9646c7
channels have been completely configured.
Packit Service 9646c7
Packit Service 9646c7
After C<buffer_messages(@types)> has been called, messages sent with
Packit Service 9646c7
C<msg> to a channel whose type is listed in C<@types> will be stored in a
Packit Service 9646c7
list for later processing.
Packit Service 9646c7
Packit Service 9646c7
This backlog of messages is processed when C<flush_messages> is
Packit Service 9646c7
called, with the current channel options (not the options in effect,
Packit Service 9646c7
at the time of C<msg>).  So for instance, if some channel was silenced
Packit Service 9646c7
in the meantime, messages to this channel will not be printed.
Packit Service 9646c7
Packit Service 9646c7
C<flush_messages> cancels the effect of C<buffer_messages>.  Following
Packit Service 9646c7
calls to C<msg> are processed immediately as usual.
Packit Service 9646c7
Packit Service 9646c7
=cut
Packit Service 9646c7
Packit Service 9646c7
sub buffer_messages (@)
Packit Service 9646c7
{
Packit Service 9646c7
  foreach my $type (@_)
Packit Service 9646c7
    {
Packit Service 9646c7
      $buffering{$type} = 1;
Packit Service 9646c7
    }
Packit Service 9646c7
}
Packit Service 9646c7
Packit Service 9646c7
sub flush_messages ()
Packit Service 9646c7
{
Packit Service 9646c7
  %buffering = ();
Packit Service 9646c7
  foreach my $args (@backlog)
Packit Service 9646c7
    {
Packit Service 9646c7
      &msg (@$args);
Packit Service 9646c7
    }
Packit Service 9646c7
  @backlog = ();
Packit Service 9646c7
}
Packit Service 9646c7
Packit Service 9646c7
=item C<setup_channel_queue ($queue, $key)>
Packit Service 9646c7
Packit Service 9646c7
Set the queue to fill for each channel that is ordered,
Packit Service 9646c7
and the key to use for serialization.
Packit Service 9646c7
Packit Service 9646c7
=cut
Packit Service 9646c7
sub setup_channel_queue ($$)
Packit Service 9646c7
{
Packit Service 9646c7
  my ($queue, $key) = @_;
Packit Service 9646c7
  foreach my $channel (keys %channels)
Packit Service 9646c7
    {
Packit Service 9646c7
      setup_channel $channel, queue => $queue, queue_key => $key
Packit Service 9646c7
        if $channels{$channel}{'ordered'};
Packit Service 9646c7
    }
Packit Service 9646c7
}
Packit Service 9646c7
Packit Service 9646c7
=item C<pop_channel_queue ($queue)>
Packit Service 9646c7
Packit Service 9646c7
pop a message off the $queue; the key has already been popped.
Packit Service 9646c7
Packit Service 9646c7
=cut
Packit Service 9646c7
sub pop_channel_queue ($)
Packit Service 9646c7
{
Packit Service 9646c7
  my ($queue) = @_;
Packit Service 9646c7
  return _dequeue ($queue);
Packit Service 9646c7
}
Packit Service 9646c7
Packit Service 9646c7
=back
Packit Service 9646c7
Packit Service 9646c7
=head1 SEE ALSO
Packit Service 9646c7
Packit Service 9646c7
L<Autom4te::Location>
Packit Service 9646c7
Packit Service 9646c7
=head1 HISTORY
Packit Service 9646c7
Packit Service 9646c7
Written by Alexandre Duret-Lutz E<lt>F<adl@gnu.org>E<gt>.
Packit Service 9646c7
Packit Service 9646c7
=cut
Packit Service 9646c7
Packit Service 9646c7
1;
Packit Service 9646c7
Packit Service 9646c7
### Setup "GNU" style for perl-mode and cperl-mode.
Packit Service 9646c7
## Local Variables:
Packit Service 9646c7
## perl-indent-level: 2
Packit Service 9646c7
## perl-continued-statement-offset: 2
Packit Service 9646c7
## perl-continued-brace-offset: 0
Packit Service 9646c7
## perl-brace-offset: 0
Packit Service 9646c7
## perl-brace-imaginary-offset: 0
Packit Service 9646c7
## perl-label-offset: -2
Packit Service 9646c7
## cperl-indent-level: 2
Packit Service 9646c7
## cperl-brace-offset: 0
Packit Service 9646c7
## cperl-continued-brace-offset: 0
Packit Service 9646c7
## cperl-label-offset: -2
Packit Service 9646c7
## cperl-extra-newline-before-brace: t
Packit Service 9646c7
## cperl-merge-trailing-else: nil
Packit Service 9646c7
## cperl-continued-statement-offset: 2
Packit Service 9646c7
## End: