NAME

Errno::AnyString - put arbitrary strings in $!

VERSION

Version 0.04

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

DESCRIPTION

If Errno::AnyString is loaded, $! behaves as normal unless a custom error string has been set with custom_errstr. If a custom error string is set, it will be returned when $! is evaluated as a string, and 458513437 will be returned when $! is evaluated as a number, see ERRSTR_SET below.

EXPORTS

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

custom_errstr ( ERROR_STRING )

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

ERRSTR_SET

ERRSTR_SET is a numeric constant with the value 458513437. This is the value that will be obtained when $! is evaluated in a numeric context while a custom error string is set.

INTERNALS

BACKGROUND

Perl scalars can hold both a string and a number, at the same time. Normally these are different representations of the same thing, for example a scalar might have 123 in its number slot and "123" in its string slot. However, it is possible to put completely unrelated things in the string and number slots of a scalar. "dualvar" in Scalar::Util allows you to do this from within Perl code:

use Scalar::Util qw/dualvar/;

my $foo = dualvar 10, "Hello";
print "$foo\n";               # prints Hello
print 0+$foo, "\n";           # prints 10

# The dual values are preserved when scalars are copied around:
my $bar = $foo;
my %hash = ( foo => $bar );
my @array = ( $hash{foo} );
print "$array[0]\n";          # prints Hello
print 0+$array[0], "\n";      # prints 10

At the C level, there is a global integer variable called errno, the "error number". Many library functions set this value when they fail, to indicate what went wrong. There is a library function to translate errno values into error message strings such as No such file or directory, Permission denied, etc. Perl's special $! variable gives access to errno from within Perl code. It uses the dual value property of scalars to return both the errno value and the corresponding error message. For example:

open my $fh, "<", "/there/is/no/such/file" or die "open: $!";

Here Perl calls a C library function to try to open the file. Within the C library, the operation fails and errno is set to the value that indicates no such file or directory (2 on my system). The library function returns a value that indicates failure, which causes Perl's open() to return false. The code above then reads the $! variable. The read triggers some magic, which copies errno into $!'s number slot , looks up the error message for error number 2, and sets $!'s string value to "No such file or directory". The value of $! is then used in a string, so the number value of 2 is ignored and the string value of "No such file or directory" is incorporated into the die message.

Perl also allows you to assign a number to $!, to set errno:

$! = 2;

This code stores a value in $!, which triggers some magic that converts the value to an integer if necessary and then copies the value to errno. Since errno is an integer, you can't put an error message of your own into $!. If you try, Perl will convert your message string to an integer as best it can, and store that in errno.

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

DESIGN GOALS

This module makes a global change to Perl's behavior when it is loaded, by interfering with the magical properties of $!. The primary design goal is compatibility. Any code that works without Errno::AnyString loaded should work just the same with Errno::AnyString loaded. Module authors should be able to be confident that pulling in Errno::AnyString to allow them to put arbitrary strings in $! is very unlikely to break anything that might ever be used in conjunction with their module.

IMPLEMENTATION

This module works by removing Perl's magic from $!, and making it into a tied scalar. The magic that would normally be on $! is put on another variable, which the tied scalar uses to access errno.

When a scalar is assigned to the tied $!, its numeric value is inspected. If it's equal to ERRSTR_SET then the string value is stashed away as the custom error string, replacing any previous custom error string. In any case, errno is set to the numeric value of the scalar.

The custom_errstr() function returns a scalar with ERRSTR_SET in the number slot and the specified error message in the string slot. Hence assigning the return value of custom_errstr() to $! sets errno to ERRSTR_SET and overwrites the stashed custom error string.

This mechanism for setting the custom error string was chosen because it works correctly with code that saves and later restores the value of $!.

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

This code works as expected under Errno::AnyString, even if a custom error string is set, and even if code called from do_other_things() sets other custom error strings. The first line saves both the numeric errno value and the error string (which will be either one of the standard system error message or a custom error string) in $saved_errno. The final line restores the saved errno value, and if that value is ERRSTR_SET it also restores the stashed custom error string.

When the tied $! is read, the numeric value of the returned scalar is fetched from errno. If it's equal to ERRSTR_SET then the stashed custom error string is returned in the string slot, otherwise the system error message corresponding to errno is returned.

INTER-OPERATION

Other modules that make changes to the way $! works should use the following methods only to interact with Errno::AnyString. These will always work, even if the underlying mechanism changes.

To undo the changes to $! if they are already in place (and to prevent the changes if Errno::AnyString is loaded later):

$Errno::AnyString::do_not_init = 1;
if (exists &Errno::AnyString::go_away) {
    &Errno::AnyString::go_away;
}

If you disable Errno::AnyString in this way, you become responsible for ensuring that the right thing happens when an SV holding a Errno::AnyString custom error string is assigned to $!. A custom error string SV will have the numeric value 458513437, and its string value will hold the error string.

If Errno::AnyString has been loaded, then a reference to the equivalent of the normal Perl $! will be returned by &Errno::AnyString::real_errno. To get a reference to the normal $! whether or not Errno::AnyString is loaded:

my $eref = exists &Errno::AnyString::real_errno ? &Errno::AnyString::real_errno : \$!;

AUTHOR

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

BUGS AND LIMITATIONS

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

If you're reading this page because something broke and spat out the above error message and you googled for it, please get in touch with me and I'll try to help.

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, perltie

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.