NAME

Errno::AnyString - put arbitrary strings in $!

VERSION

Version 1.00

SYNOPSIS

Errno::AnyString allows you to place an arbitrary error message in the special $! variable, without disrupting $!'s ability to pick up the result of the next system call that sets errno.

It is useful if you are writing code that reports errors by setting $!, and none of the standard system error messages fit.

use Errno qw/EIO/;
use Errno::AnyString qw/custom_errstr/;

$! = custom_errstr "My hovercraft is full of eels";
print "$!\n"; # prints My hovercraft is full of eels

my $saved_errno = $!;

open my $fh, "<", "/no/such/file";
print "$!\n"; # prints No such file or directory

$! = EIO;
print "$!\n"; # prints Input/output error

$! = $saved_errno;
print "$!\n"; # prints My hovercraft is full of eels

You can also set the error strings for particular error numbers, for the lifetime of the Perl interpreter:

use Errno::AnyString qw/register_errstr/;

register_errstr "Wetware failure", 339864;

$! = 339864;
print "$!\n"; # prints Wetware failure

BACKGROUND

Perl's special $! variable provides access to errno, the "error number", which is an integer variable used by C library functions to record what went wrong when they fail. See "ERRNO" in perlvar and Errno.

There is a fixed error message for each errno value in use, and a C library function to translate errno values into error messages. The magical $! variable always holds the current value of errno if you use it in a numeric context, and the corresponding error message if you use it in a string context.

open my $fh, "<", "/no/such/file"; # the failing open sets errno to 2
my $errno = $! + 0;  # $errno now contains 2
my $err = "$!";      # $err now contains "No such file or directory"

You can also assign a number to $!, to set the value of errno. An errno value of 22 means "invalid argument", so:

$! = 22;
$errno = $! + 0;  # $errno now contains 22
$err = "$!";      # $err now contains "Invalid argument"

What you can't do however is assign a string of your own choice to $!. If you try, Perl just converts your string to an integer as best it can and puts that in errno.

$! = "You broke it";
# gives an "Argument isn't numeric" warning and sets errno to 0

DESCRIPTION

Errno::AnyString allows you to set the error message strings that correspond to particular errno values. It makes a change to the $! magic so that the correct string is returned when errno takes a value for which a string has been registered. The change to $! is global and lasts until the Perl interpreter exits.

EXPORTS

Nothing is exported by default. The following are available for export.

CUSTOM_ERRSTR_ERRNO

A constant with the value 458513437. This is the errno value used by this module to indicate that a custom error string set with custom_errstr() is active. This value was chosen at random, to avoid picking an errno value that something else uses.

custom_errstr ( ERROR_STRING )

Returns a value which will set the specified custom error string when assigned to $!.

The returned value is actually a dual valued scalar with CUSTOM_ERRSTR_ERRNO as its numeric value and the specified error string as its string value. It's not just magical variables like $! that can hold a number and a string at the same time, ordinary Perl scalars can do it as well. See "dualvar" in Scalar::Util.

With Errno::AnyString loaded, the $! magic responds specially to a scalar with a numeric value of CUSTOM_ERRSTR_ERRNO being assigned to $!: the string value of the scalar gets recorded as the registered string for errno value CUSTOM_ERRSTR_ERRNO, replacing any previous registered string for that value.

This way of setting the custom error string was chosen because it works well with code that saves and restores the value of $!.

$! = custom_errstr "Test string";

my $saved_errno = $!;
do_other_things();
$! = $saved_errno;

print "$!\n"; # prints Test string

When $! is copied to $saved_errno, $saved_errno becomes dual valued with a number value of CUSTOM_ERRSTR_ERRNO and a string value of "Test string". When $saved_errno gets copied back to $! at the end, the number value of CUSTOM_ERRSTR_ERRNO triggers the modified $! magic to register the string value of "Test string" as the custom error string for CUSTOM_ERRSTR_ERRNO.

This is important because code called from within do_other_things() might itself use custom_errstr() to set custom error strings, overwriting the registered error string of "Test string". Since $saved_errno saves the error message string as well as the errno value, the $! magic can put the correct string back in place when the $saved_errno value is restored.

register_errstr ( ERROR_STRING [,ERRNO_VALUE] )

register_errstr() can be used in a similar way to custom_errstr():

$! = register_errstr "An error string";

The difference is that register_errstr() permanently (i.e. for the lifetime of the Perl interpreter) assigns an errno value to that error string. The error string is stored away, and will be used as the string value of $! any time that errno value is set in future. By default, register_errstr() picks a large errno value that it has not yet assigned to any other string.

If you call register_errstr() repeatedly with the same error string, it will notice and use the same errno value each time. That means it's safe to do something like:

$! = register_errstr "Too many foos defined";

in code that could be called a large number of times, and register_errstr() will store only one copy of the string and use up only one errno value.

You can specify the errno value to use as a second parameter to register_errstr(), for example:

$! = register_errstr "my error string", 999999;

This sets the error string for errno value 999999 (replacing any previously set error string for 999999) and assigns it to $!. You can also call register_errstr() simply to register a bunch of new error codes, without assigning the return value to $! each time:

register_errstr "Invalid foodb file",      -20000;
register_errstr "Invalid foodb parameter", -20001;
register_errstr "foodb out of key slots",  -20002;

# ...

$! = -20001;
print "$!\n"; # prints Invalid foodb parameter

It is also possible to use register_errstr() to replace the standard system error messages. For example, to replace the "Permission denied" message;

use Errno qw/EACCES/;
register_errstr "I'm sorry, Dave. I'm afraid I can't do that", EACCES;

open my $fh, ">/no_permission_to_write_here";
print "$!\n"; prints "I'm sorry, Dave. I'm afraid I can't do that"

This is not something I'd recommend, as it's likely to cause confusion. In general, when specifying the errno value to register_errstr() one should take care to avoid values that are likely to be used for any other purpose.

Internally, the error strings registered by register_errstr() are kept in the %Errno::AnyString::Errno2Errstr hash. You shouldn't go poking around in this hash yourself, but by localising it you can limit the scope of a register_errstr() registration:

{
    local %Errno::AnyString::Errno2Errstr = %Errno::AnyString::Errno2Errstr;

    register_errstr "I'm sorry, Dave. I'm afraid I can't do that", EACCES;

    # here you have a silly error message in place of "Permission denied"
}
# here sanity is restored

INTER-OPERATION

This section is aimed at the authors of other modules that alter $!'s behaviour, as a guide to ensuring clean inter-operation between Errno::AnyString and your module.

Errno::AnyString works by adding two instances of uvar magic to $!, one at the head of the list and one at the tail. It does not modify or remove any existing magic from $!. It should inter-operate cleanly with anything else that adds more magic to $!, so long magic is added in a way that preserves existing uvar magic.

Emptying the %Errno::AnyString::Errno2Errstr hash effectively turns off this module's interference with $!, so you can get a "real" $! value with:

my $e = do { local %Errno::AnyString::Errno2Errstr ; $! };

AUTHOR

Dave Taylor, <dave.taylor.cpan at gmail.com>

BUGS AND LIMITATIONS

C LEVEL STRERROR CALLS

If C level code attempts to get a textual error message based on errno while a custom error string is set, it will get something like the following, depending on the platform:

Unknown error 458513437

PURE NUMERIC RESTORE

If the string part of a saved custom_errstr() $! value is lost, then restoring that value to $! restores the string most recently set with custom_errstr(), which is not necessarily the string that was set when the $! value was saved.

$! = custom_errstr "String 1";
my $saved_errno = 0 + $!;

$! = custom_errstr "String 2";

$! = $saved_errno;
print "$!\n"; # prints String 2

Note that the Perl code that saved the error number had to go out of its way to discard the string part of $!, so I think this combination is fairly unlikely in practice.

Error strings set with register_errstr() are not effected by this issue, since each gets its own unique errno value. For this reason, register_errstr() should be used in preference to custom_errstr() if you have a small number of fixed error strings:

$! = register_errstr "Attempt to frob without a foo"; # good

However, register_errstr() uses up an errno value and permanently stores the string each time it is called with a string it has not seen before. If your code could generate a large number of different error strings over the lifetime of the Perl interpreter, then using register_errstr() could cost a lot of memory. In such cases, custom_errstr() would be a better choice.

$! = register_errstr "failed at $line in $file: $why"; # less good 

TAINT MODE

I'm currently unable to find a good way to propagate the taintedness of custom error strings through $!, due to an interaction between taint magic, $!'s dualvar behaviour and the SvPOK flag in some Perl versions. If Perl is in taint mode then passing a tainted error string to custom_errstr() or register_errstr() will cause an immediate croak with the message:

Tainted error string used with Errno::AnyString

OTHER BUGS

Please report any other bugs or feature requests to bug-errno-anystring at rt.cpan.org, or through the web interface at http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Errno::AnyString. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.

SUPPORT

You can find documentation for this module with the perldoc command.

perldoc Errno::AnyString

You can also look for information at:

SEE ALSO

Errno, "ERRNO" in perlvar, Scalar::Util, perlguts

COPYRIGHT & LICENSE

Copyright 2009 Dave Taylor, all rights reserved.

This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.