Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Please mail me the proposed perlopentut

26 views
Skip to first unread message

Tom Christiansen

unread,
Feb 14, 2013, 7:56:04 AM2/14/13
to Perl5 Porters Mailing List, Nicholas Clark, Dave Rolsky, demerphq, Ricardo Signes, H.Merijn Brand
This is tchrist in exile. My incoming email works,
but not my outgoing nor various other networking.

==================================================================
Please mail me a copy of this proposed revision to perlopentut.
==================================================================

You can send it to my regular tch...@perl.com addess.
I don’t know when I will have outgoing mail fixed.

Please ***do not*** in a fit of PC-madness change all the

open(...) || die;

There is nothing wrong with that, and many Perl programmers
use it in perfect safety and predictability.

perlopentut is written in a self-consistent fashion that
makes sense. Change that self-consistency and you risk
destroying the sense.

--tom




Tom Christiansen

unread,
Feb 14, 2013, 2:59:54 PM2/14/13
to Ricardo Signes, Perl5 Porters Mailing List
The current version no longer makes sense.

You cannot just move material around that depends on
other material. For example, the indirect handle
material relies on the direct handles already having
been explained. Now you have broken the references.

This just does not work this way.

Also, implicit close is a bug, not a feature. Don't
push it. Don't use it. Always check the close.

The spacing and quoting and operators are all completely
messed up and alien to me. I realize that doesn't matter,
but this is not "modernizing". This is just running through
somebody else's perl tidy or perl critic settings. Senseless
political correctness gone mad. Aren't there more important
things to do?

--tom

Jesse Luehrs

unread,
Feb 14, 2013, 3:19:25 PM2/14/13
to Tom Christiansen, Ricardo Signes, Perl5 Porters Mailing List
On Thu, Feb 14, 2013 at 12:12:31PM -0800, Tom Christiansen wrote:
> The very least you could do is run it through diction.
>
> Almost all of the 78 of the XXX's below need fixing.

Throwing this wall of text out here without any kind of checking to see
if it actually makes sense (quite a few of these changes are
entirely nonsensical) is *incredibly* unhelpful.

-doy

Steffen Mueller

unread,
Feb 14, 2013, 4:14:13 PM2/14/13
to Tom Christiansen, Ricardo Signes, Perl5 Porters Mailing List
On 02/14/2013 08:59 PM, Tom Christiansen wrote:
> Also, implicit close is a bug, not a feature. Don't
> push it. Don't use it. Always check the close.

+1 on this point. Even though I am very guilty of omission myself.

--Steffen

Dave Rolsky

unread,
Feb 14, 2013, 5:46:34 PM2/14/13
to Ricardo Signes, perl5-...@perl.org
On Thu, 14 Feb 2013, Ricardo Signes wrote:

> I don't think I agree. Imagine the same attachment prefaced by:
>
> In addition to the criticisms in my previous message, a lot could be gained
> by running the document through the Unix `diction` program, which often has
> useful advice. In case you don't have it, here's the sample output. I
> skimmed it, and most of it's good.
>
> I think I've summed up why Tom sent it, and I found it useful. The only
> unfortunate part was that the wall of text seemed, at first, to overwhelm the
> most useful point: `diction` exists!

Well, rather than resending the whole thing how about using that patch
program some guy wrote (Barry? Harry? Wario?).


-dave

/*============================================================
http://VegGuide.org http://blog.urth.org
Your guide to all that's veg House Absolute(ly Pointless)
============================================================*/

Rob Dixon

unread,
Feb 15, 2013, 5:28:06 AM2/15/13
to Perl5 Porters Mailing List, Tom Christiansen
On 14/02/2013 19:59, Tom Christiansen wrote:
>
> Also, implicit close is a bug, not a feature. Don't
> push it. Don't use it. Always check the close.

I wonder if you could explain your reasons for this Tom? Are you saying
that the proper way to code is with an explicit close on every file
handle? My instinct is to disagree with you, but I woukld like to hear
what you have to say first.

What would be a better alternative to closing and releasing a file
handle that is garbage-collected? Do you think it's appropriate to make
this a hard error and raise an exception? I would have thought the way
things are, especially with read-only file handles, was the best solution.

Rob

Ed Avis

unread,
Feb 15, 2013, 6:40:32 AM2/15/13
to perl5-...@perl.org
>
Hi, may I add some comments on the new draft perlopentut?

I understand it is still work in progress so it is a bit unfair to nitpick it
to the same standard as a finished document, and of course many others could
spot the same things I noticed, but still I hope some of these comments
will be useful.

>Ignoring surrounding whitespace also helps for when you read a filename
>in from a different file, and forget to trim it before opening:
>
> $filename = <$info>; # oops, \n still there
> open(my $extra, '<', $filename)
> or die "can't open $filename: $!";
>
>This is not a bug, but a feature. Because C<open> mimics the shell in
>its style of using redirection arrows to specify how to open the file, it
>also does so with respect to extra whitespace around the filename itself
>as well.

This is not true as far as I can tell:

% perl -E 'open my $fh, "<", "/etc/passwd " or die $!'

The extra space is not trimmed; the filename is opened as-is. And *that*
is not a bug, but a feature ;-).

>=head2 Indirect Filehandles
>
>C<open>'s first argument can be a reference to a filehandle. If the argument
>is uninitialized, Perl will automatically create a filehandle and put a
>reference to it in the first argument,

This section made sense when the earlier part of the document used barewords.
But now that it uses variable names as filehandles throughout, you don't need
to mention it - but you could add a section at the end mentioning the older
bareword style.

>Another convenient behavior is that an indirect filehandle automatically
>closes when there are no more references to it:
>
> sub firstline {
> open( my $in, '<', shift ) && return scalar <$in>;
> # no close() required
> }

As tchrist noted, this does need mentioning - but whether one should use this
'convenient behavior' is a matter of opinion.

(As a general point, I think it better to use more neutral language throughout,
saying 'An indirect filehandle automatically closes' rather than adding the
sales patter about how convenient it is. A lot of the beginner docs have a
'rah rah Perl' flavour and it sometimes sounds off-putting.)

>Indirect filehandles also make it easy to pass filehandles to and return
>filehandles from subroutines:

Again, now that the new style is used throughout, you don't need to mention
this - I think most beginners will just assume that they can pass filehandles
around like any other variable.

>If the mode character is a pipe symbol followed by a dash, C<open> starts up
>a new command and opens a write-only filehandle leading into that command.

>But if the command contains special shell characters, such as
>C<E<gt>> or C<*>, called 'metacharacters', Perl does not execute the
>command directly. Instead, Perl runs the shell,

This is true in the special case of a single scalar for the command, but not
true if the command has arguments given. For example

% perl -E 'open(my $fh, "-|", "echo *"); while (<$fh>) { print }'
[lists the current directory]
% perl -E 'open(my $fh, "-|", "echo", "*"); while (<$fh>) { print }'
*

>If you would like to open a bidirectional pipe, the IPC::Open2
>library will handle this for you.

I'd also mention IPC::Open3.

>=head2 The Minus File
>
>Again following the lead of the standard shell utilities, Perl's
>C<open> function treats a file whose name is a single minus, "-", in a
>special way.

Not true in the case of 3-arg open:

% perl -E 'open(my $fh, "<", "-") or die $!'
No such file or directory at -e line 1.

I believe it will correctly open a file called '-'.

>When you process the ARGV filehandle using
>C<< <ARGV> >>, Perl actually does an implicit open
>on each file in @ARGV.

Worth noting that this is a 2-arg open with all the accompanying magic.
Therefore, this section needs to come after 2-arg open is introduced.

>=head2 Dispelling the Dweomer

I think this section is mostly unnecessary (in a *beginner* tutorial)
because 3-arg open takes care of it.

>If you aren't running with C<strict refs>, or if you've turned them off
>temporarily, then all you have to do is this:
>
> open($path '<', $path)
> or die "can't open $path: $!";

A neat trick (though I think your version has lost a comma above).
Is there a way to do this while still getting the benefit of 'strict refs'?

>To open a directory, you should use the C<opendir> function, then
>process it with C<readdir>, carefully restoring the directory
>name if necessary:

It's good to have opendir and readdir in this tutorial, but perhaps beginners
would be better served by glob('*')?

--
Ed Avis <e...@waniasset.com>

Christian Millour

unread,
Feb 15, 2013, 8:29:21 AM2/15/13
to perl5-...@perl.org
Le 15/02/2013 11:28, Rob Dixon a écrit :
> On 14/02/2013 19:59, Tom Christiansen wrote:
>>
>> Also, implicit close is a bug, not a feature. Don't
>> push it. Don't use it. Always check the close.
>
> I wonder if you could explain your reasons for this Tom? Are you saying
> that the proper way to code is with an explicit close on every file
> handle? My instinct is to disagree with you, but I woukld like to hear
> what you have to say first.
>

I would half agree with Tom here. I always spell out and check the close
on file handles opened for writing (think buffered writes and no space
left), as even autodie generally cannot help here (the problem is caught
too late to be useful). I dispense with it for file handles opened for
reading because I am yet to experience a close error in that case. YMMV.

Rob Dixon

unread,
Feb 15, 2013, 8:43:06 AM2/15/13
to perl5-...@perl.org, Christian Millour
On 15/02/2013 13:29, Christian Millour wrote:
> Le 15/02/2013 11:28, Rob Dixon a écrit :
>> On 14/02/2013 19:59, Tom Christiansen wrote:
>>>
>>> Also, implicit close is a bug, not a feature. Don't
>>> push it. Don't use it. Always check the close.
>>
>> I wonder if you could explain your reasons for this Tom? Are you saying
>> that the proper way to code is with an explicit close on every file
>> handle? My instinct is to disagree with you, but I woukld like to hear
>> what you have to say first.
>>
>
> I would half agree with Tom here. I always spell out and check the close
> on file handles opened for writing (think buffered writes and no space
> left), as even autodie generally cannot help here (the problem is caught
> too late to be useful). I dispense with it for file handles opened for
> reading because I am yet to experience a close error in that case. YMMV.

That is exactly the situation where I /would/ explicitly close a file
handle, and I can think of none other.

Rob

Tom Christiansen

unread,
Feb 15, 2013, 10:43:16 AM2/15/13
to Perl5 Porters Mailing List, Rob Dixon, tch...@perl.com, Ricardo Signes
On Fri, 2/15/13, Rob Dixon <rob....@gmx.com> wrote, quoting me:

>> Also, implicit close is a bug, not a feature.  Don't push it.
>> Don't use it.  Always check the close.

> I wonder if you could explain your reasons for this Tom? Are
> you saying that the proper way to code is with an explicit
> close on every file handle? My instinct is to disagree with
> you, but I woukld like to hear what you have to say first.

Every single system call whose formal definition includes the possibility
of failing and setting errno must always be checked for that condition.
No exceptions. Just do it. I'm serious. Please read Darwin and Collyer
in marwww.in2p3.fr/~hoffmann/pv/canthappen.pdf

Can’t Happen
or
/* NOTREACHED */
or
Real Programs Dump Core

ABSTRACT:

UNIX programmers too often fail to check for failure of system calls
or functions, taking the familiar teen-age attitude that “it can’t
happen to me (or my program)” This paper will attempt to convince its
audience to take prophylactic measures. Those who take such measures
will be healthier − and less prone to surprises − than those who don’t
take such measures.

(...)

1.2.2. System Calls

In what program do we find the following code sequence:

open("/", 0);
dup(0);
dup(0);

V7 init, alas. There are two other occurrences, in which the opens
are ”open(tty, 2);” and ”open(ctty, 2);”. The author knew, by god,
that those system calls could never fail. As a result, when the
kernel file or inode tables fill, init fails to re-populate some
terminals with init children and thus getty’s. Thus those terminals
will never inherit init’s until the next crash or reboot. We know:
the file and inode tables aren’t supposed to fill, thus it can’t
happen. A common counter-argument is that in such a case there is
nothing sensible to be done. Yet a moment’s thought often reveals a
better alternative than failing to check. In this case, since the
code in question is running in a child of init, init can sleep
briefly and try again if a system call such as dup fails or if an
open fails due to resource exhaustion.

> What would be a better alternative to closing and releasing a file
> handle that is garbage-collected?

Implicit, unchecked close is a bug.

> Do you think it's appropriate to make this a hard error and raise an
> exception?

In the absence of an operative autodie pragma, it should certainly be
nothing less than

(S) A severe warning (enabled by default).

And with autodie, it should be:

(F) A fatal error (trappable).

> I would have thought the way things are, especially with
> read-only file handles, was the best solution.

No, that simply is wrong. What you are saying is that you do not
care whether the read operation actually did what it was asked to
do. That is a dangerously cavalier attitude that can and does lead
to serious bugs. The read-only open(2) of slash above in init(8)
was not checked for error, nor were the dup(2) syscalls.

Let's say you read from one file and do something based on what
you read from that file. Don't you think it matters when you
read the first ten out of a hundred lines and then got an I/O
error? The error bit will be set on the handle, just as it is
when you have a buffer-delayed fflush(3)/fpurge(3) on the FILE*.
These happen on reads, too. Either way it means means when you
get around to the fclose(3), it will return the obligate error.

You *have* to check for that. The only exception is when you do
not care about correctness. If that is the case, then don't run
the program at all, since its "it ran correctly" status does not
appear to concern you.

Consult your local read(2) and close(2) manpages. You will find
many, many, many possible scenarios for those system calls to fail.
Just on the read(2) alone, you will find things like this:

[EAGAIN] The file was marked for non-blocking I/O,
and no data were ready to be read.
[EBADF] fildes is not a valid file or socket descriptor
open for reading.
[ECONNRESET] The connection is closed by the peer during
a read attempt on a socket.
[EFAULT] Buf points outside the allocated address space.
[EINTR] A read from a slow device was interrupted before
any data arrived by the delivery of a signal.
[EINVAL] The pointer associated with fildes was negative.
[EIO] An I/O error occurred while reading from the file system.
[EIO] The file is a regular file, nbyte is greater than 0,
the starting position is before the end-of-file,
and the starting position is greater than or
equal to the offset maximum established for the
open file descriptor associated with fildes.
[EIO] The process group is orphaned.
[EISDIR] An attempt is made to read a directory.
[ENOBUFS] An attempt to allocate a memory buffer fails.
[ENOMEM] Insufficient memory is available.
[ENOTCONN] A read is attempted on an unconnected socket.
[ENXIO] A requested action cannot be performed by the device.
[ENXIO] An action is requested of a device that does not exist.
[ETIMEDOUT] A transmission timeout occurs during a read attempt on a socket.

Those matter. If a system call *can* fail, you *must* check for that failure
each and every single possible time. It does not matter whether the handle is
read-only. Check its close. Always. No matter what.

Implicit close is a bug, not a feature.

--tom

Ed Avis

unread,
Feb 15, 2013, 11:37:06 AM2/15/13
to perl5-...@perl.org
Tom Christiansen <boulderad0 <at> yahoo.com> writes:

>If a system call *can* fail, you *must* check for that failure
>each and every single possible time. It does not matter whether the handle is
>read-only. Check its close. Always. No matter what.

I agree that you should check close() but isn't it better to check at the point
where you read() from the filehandle rather than waiting until later? What
can we as Perl programmers do to make sure that every read() is checked - and
error conditions are reported appropriately unless explicitly handled - while
still keeping our code concise and Perlish?

For example, is the common idiom

while (<$fh>) { print }

safe against all errors returned from reading the filehandle?

--
Ed Avis <e...@waniasset.com>

Rob Dixon

unread,
Feb 15, 2013, 12:33:55 PM2/15/13
to Perl5 Porters Mailing List, Tom Christiansen, tch...@perl.com, Ricardo Signes
On 15/02/2013 15:43, Tom Christiansen wrote:
> On Fri, 2/15/13, Rob Dixon <rob....@gmx.com> wrote, quoting me:
>
>>> Also, implicit close is a bug, not a feature. Don't push it.
>>> Don't use it. Always check the close.
>
>> I wonder if you could explain your reasons for this Tom? Are
>> you saying that the proper way to code is with an explicit
>> close on every file handle? My instinct is to disagree with
>> you, but I woukld like to hear what you have to say first.
>
>
>> I would have thought the way things are, especially with
>> read-only file handles, was the best solution.
>
> No, that simply is wrong. What you are saying is that you do not
> care whether the read operation actually did what it was asked to
> do. That is a dangerously cavalier attitude that can and does lead
> to serious bugs. The read-only open(2) of slash above in init(8)
> was not checked for error, nor were the dup(2) syscalls.
>
> Let's say you read from one file and do something based on what
> you read from that file. Don't you think it matters when you
> read the first ten out of a hundred lines and then got an I/O
> error? The error bit will be set on the handle, just as it is
> when you have a buffer-delayed fflush(3)/fpurge(3) on the FILE*.
> These happen on reads, too. Either way it means means when you
> get around to the fclose(3), it will return the obligate error.
>
> You *have* to check for that. The only exception is when you do
> not care about correctness. If that is the case, then don't run
> the program at all, since its "it ran correctly" status does not
> appear to concern you.

What you are saying has far more important implications regarding
`readline` than `close`. I had imagined - without really thinking about
it - that Perl was doing the Right Thing with reads, and if a loop
like

while (<$fh>) { ... }

terminated for any reason other than end of file then it would be
reported. However tests show that that isn't true, and should be
checking `$!` after every such loop to make sure that things went well.

This is far more important than an implicit `close`, as `readline` and
`print` will silently operate on a failing file handle without comment.
If a file is sucessfully read to its eof then I care little about
whether a subsequent `close` is successful. Likewise if my final `print`
succeeds, then the only remaining hurdle is to flush the buffer, after
which everything is secure.

If I write to a full disk volume then my `print` statements will remain
silent, but a subsequent `close $fh or die $!` will also **never produce
a diagnostic message**. What happens is that we are given a cryptic

Can't close(GLOB(0x63cf24)) filehandle: ''

and a code 255 termination. So there is never a chance to trap a failed
close unless it is the buffer flush that breaks the Camel's back when
the we *do* get a chance to retrieve the status and get

Can't close(GLOB(0x63cf24)) filehandle: 'No space left on device'

So at least the closure - whether it is explicit or implict - reports
that something has gone wrong, whereas the actual file operations
blithely continue unless their status is explicitly checked.

To me this seems to require some thinking about `print` and `readline`,
with `close` coming a close second - at least with regard to being able
to trap its errors.

Rob

Tom Christiansen

unread,
Feb 15, 2013, 1:03:28 PM2/15/13
to Ed Avis, perl5-...@perl.org
Ed Avis <e...@waniasset.com> wrote:

>> If a system call *can* fail, you *must* check for that failure each
>> and every single possible time. It does not matter whether the
>> handle is read-only. Check its close. Always. No matter what.

> I agree that you should check close() but isn't it better to check at the point
> where you read() from the filehandle rather than waiting until later? What
> can we as Perl programmers do to make sure that every read() is checked - and
> error conditions are reported appropriately unless explicitly handled - while
> still keeping our code concise and Perlish?

> For example, is the common idiom
>
> while (<$fh>) { print }
>
> safe against all errors returned from reading the filehandle?

Excellent question.

No, it isn't, since the undef return from a read failure is
otherwise indistinguishable from the undef return because a
failure in the implicit close is not detected and reported.

You can check that manually this way after the loop is over
(provided you haven't called $fh->clearerr, of course):

if ($fh->error) {
warn "error after raeding from filehandle $fh\n";
}

But you cannot be assured that errno will be set correctly;
it almost never will. That is why I did not print it out.

I haven't been daring enough to chasing through all the indirection in
perlio.c to see whether the things that PerlIO_error() returns through
the function pointers are in actual fact a more interesting flavor of
true than the base documentation describes.

However, I suspect but do not know that fclose() *will* transmit
the errno correctly if an error condition exists on the handle
being closed.

For correct behavior in the most general of cases, you really
have to add:

close($fh) || die "couldn't close somefh opened for reading: $!";
close(STDOUT) || die "couldn't close STDOUT opened for writing: $!";

or words to that effect.

Why? Well, imagine that $fh is reading from a popen()ed command. All
the command output you read comes through ok, but then the command dies
a miserable death of one or another sort, including simply exiting non-
zero. You will not be aware that the input source went belly up until
you call pclose() on it, with a false return from Perl close and a side-
effect of $? (or $!, or both) being set to some non-zero value.

For example:

$ perl -e 'while (<>) { print "$ARGV $.: $_" }
close(ARGV)|| die "cannot close ARGV"' "cat /et/c/miss/ing | "
cat: /et/c/miss/ing: No such file or directory
cannot close ARGV at -e line 2.

And yes, that need the close(STDOUT) test, too.

If you think these are all too tricky to get right, I probably agree. I'd
rather try to make Perl smarter than trying to remember to always be smart
myself, or even if I did manage that, than trying to get everybody else to
always be smart. Those won't happen, so let's make Perl smarter instead.

--tom

Tom Christiansen

unread,
Feb 15, 2013, 1:07:13 PM2/15/13
to Perl5 Porters Mailing List, Rob Dixon, tch...@perl.com, Ricardo Signes
> What you are saying has far more important implications regarding
> `readline` than `close`. I had imagined - without really thinking about
> it - that Perl was doing the Right Thing with reads, and if a loop
> like

while (<$fh>) { ... }

> terminated for any reason other than end of file then it would be
> reported. However tests show that that isn't true, and should be
> checking `$!` after every such loop to make sure that things went well.

Um, no. You cannot check $! to make sure that everything went well.
That is a classic errno-handling screw-up. Remember: failed syscalls
set errno, but successful ones do not clear it.

You have to check $fh->error. Or $fh->close.

--tom

Rob Dixon

unread,
Feb 15, 2013, 1:21:05 PM2/15/13
to perl5-...@perl.org, Tom Christiansen, Ed Avis
On 15/02/2013 18:03, Tom Christiansen wrote:
>
> For correct behavior in the most general of cases, you really
> have to add:
>
> close($fh) || die "couldn't close somefh opened for reading: $!";
> close(STDOUT) || die "couldn't close STDOUT opened for writing: $!";
>
> or words to that effect.

As I commented before, that doesn't appear to work, at least in the
specific case of a device full error on output. If the last `print`
succeeds but the buffer flush on closure causes an error then all is
well, but if the error is caused by a `print` then a later `close or
die` will fail, and perl will produce a generic file handle error and
exit 255;

Rob

Tom Christiansen

unread,
Feb 15, 2013, 4:18:52 PM2/15/13
to Rob Dixon, perl5-...@perl.org, Ed Avis
I see what you mean now.

(BTW, does anybody know how to mock up a /dev/full device that always
causes write operations to fail and sets errno to ENOSPC? This would
really help for testing.)

Looks like errno is doomed to getting lost, because there is no way to
know that there wasn't an intervening syscall. None of the stdio
implementations I could find seem to go to any trouble to save off that
errno for later return. For example, fread(3) has code like this
in /usr/src/lib/libc/stdio/fread.c:

if (fp->_r <= 0) {
if (fp->_r == 0)
fp->_flags |= __SEOF;
else {
fp->_r = 0;
fp->_flags |= __SERR;
}
return (EOF);
}

That's going to (idempotently) set the error bit on the flags word,
but there is nothing like a per-FILE* field that has the last errno
in it.

Perl follows the same strategy, or'ing the error bit into the flags:

PerlIOBase(f)->flags |= PERLIO_F_ERROR

but there is nothing like this sort of thing to go with it:

PerlIOBase(f)->last_errno = errno;

That's because there is no such field in a PerlIO struct. The
error condition is just a flag bit; it is not a full errno.

And even if there were, it would be a per-layer value, and I don't
know how you would go about percolating an errno from a lower layer
up through the layer stack as you closed down a handle.

Given that this currently appears to be impossible, we should come
up with some sound, reliable advice about what to check, when, and
what to report.

--tom

Alexander Hartmaier

unread,
Feb 16, 2013, 12:36:20 AM2/16/13
to Tom Christiansen, Ricardo Signes, Perl5 Porters Mailing List
On Thu, Feb 14, 2013 at 9:12 PM, Tom Christiansen <bould...@yahoo.com> wrote:
The very least you could do is run it through diction.

Almost all of the 78 of the XXX's below need fixing.

--tom

=head1 NAME

perlopentut - tutorial on opening things in Perl

=head1 DESCRIPTION

Perl has {*XXX* a number of > several, many, some *XXX*} ways to open handles to files and external programs. If
you are familiar with shell programming, much of Perl's syntax will be
familiar.

=head2 Simple Opens

The C<open> function takes three arguments: the first is a filehandle, the
second defines the mode and the third tells Perl what to open. The C<open>
builtin returns true when it succeeds and returns a false value when it
fails. When it fails, it also sets the special variable C<$!> to the value of
the system error for the failure. If the filehandle was previously opened, it
will be implicitly closed first.

For example:

    open(my $info,    '<',  'datafile')
        or die("can't open datafile: $!");
    open(my $results, '>',  'runstats')
        or die("can't open runstats: $!");
    open(my $log,     '>>', 'logfile ')
        or die("can't open logfile:  $!");

The other important thing to notice is that, just as in the shell,
any whitespace before or after the filename is ignored.


Ignoring surrounding whitespace also helps for when you read a filename
in from a different file, and forget to trim it before opening:

    $filename = <$info>;            # oops, \n still there
    open(my $extra, '<', $filename)
        or die "can't open $filename: $!";

This is not a bug, but a feature.  Because C<open> mimics the shell in
its style of using redirection arrows to specify how to open the file, it
also does so {*XXX* with respect to > about *XXX*} extra whitespace around the filename itself
as well.  For accessing files with naughty names, see
L<"Dispelling the Dweomer">.


=head2 Indirect Filehandles

C<open>'s first argument can be a reference to a filehandle. If the argument
is uninitialized, Perl will automatically create a filehandle and put a
reference to it in the first argument, like so:

    open( my $in, '<', $infile )
        or die "Couldn't read $infile: $!";
    while ( <$in> ) {
        # do something with $_
    }
    close $in;

Indirect filehandles make namespace management easier.  Since filehandles
are global to the current package, two subroutines trying to open
C<INFILE> will clash.  With two functions opening indirect filehandles
like C<my $infile>, there's no clash and no need to worry about future
conflicts.


Another convenient behavior is that an indirect filehandle automatically
closes when there are no more references to it:

    sub firstline {
        open( my $in, '<', shift ) && return scalar <$in>;
        # no close() required
    }

Indirect filehandles also make it easy to pass filehandles to and return
filehandles from subroutines:

    for my $file ( qw(this.conf that.conf) ) {
        my $fin = open_or_throw('<', $file);
        process_conf( $fin );
        # no close() needed
    }

    use Carp;
    sub open_or_throw {
        my ($mode, $filename) = @_;
        open my $fh, $mode, $filename
            or croak "Could not open '$filename': $!";
        return $fh;
    }

=head2 Pipe Opens

To get a pipe to a program, either reading or writing, you can use the dash
character in front of or after the pipe character as the mode argument.


If the mode character is a pipe symbol followed by a dash, C<open> starts up
a new command and opens a write-only filehandle leading into that command.
This lets you write into that handle and have what you write show up on
that command's standard input.  For example:

    open(my $printer, '|-', 'lpr', '-Plp1')
        or die "can't run lpr: $!";
    print $printer "stuff\n";
    close($printer)
        or die "can't close lpr: $!";

If the dash character is ahead of the pipe, you start up a new command and
open a read-only filehandle leading out of that command. This lets whatever
that command writes to its standard output show up on your handle for reading.
For example:

    open(my $net, '-|', 'netstat', '-i', '-n')
        or die "can't fork netstat: $!";
    while (<$net>) { } # do something with input
    close($net)
        or die "can't close netstat: $!";

What happens if you try to open a pipe to or from a non-existent
command?  If possible, Perl will detect the failure and set C<$!> as
usual.  But if the command contains special shell characters, such as

C<E<gt>> or C<*>, called 'metacharacters', Perl does not execute the
command directly.  Instead, Perl runs the shell, which then tries to
run the command.  This means that it's the shell that gets the error
indication.  In such a case, the C<open> call will only {*XXX* indicate > show, suggest *XXX*}
failure if Perl can't even run the shell.  See L<perlfaq8/"How can I
capture STDERR from an external command?"> to see how to cope with
this.  There's also an explanation in L<perlipc>.


If you would like to open a bidirectional pipe, the IPC::Open2
library will handle this for you.  Check out
L<perlipc/"Bidirectional Communication with Another Process">

If you already have a shell snippet you can put it into the third argument
without splitting the command and its parameters. This will invoke the shell
if shell metacharacters are found which is unsafer, slower and foremost less
portable than using the list notation but allows special shell constructs.
It is not supported on Microsoft Windows and RISC OS.

Here's an example of C<open '-|'>, which reads the contents of a gzipped tar
file into an array:

    my $file = shift(@ARGV);
    open(my $fh, '-|', "zcat ${file} | tar xfv -")
        or die "Could not get contents of compressed file: $!";
    my @filenames;
    while (<$fh>) {
        chomp;                  # remove line-feed
        push @filenames, $_;
    }
    close($fh);


=head2 The Minus File

Again following the lead of the standard shell utilities, Perl's
C<open> function treats a file whose name is a single minus, "-", in a
special way.  If you open minus for reading, it really means to access
the standard input.  If you open minus for writing, it really means to
access the standard output.

If minus can be used as the default input or default output, what happens
if you open a pipe into or out of minus?  What's the default command it
would run?  The same script as you're currently running!  This is actually
a stealth C<fork> hidden inside an C<open> call.  See
L<perlipc/"Safe Pipe Opens"> for details.

=head2 Mixing Reads and Writes

It is possible to specify both read and write access.  All you do is
add a "+" symbol in front of the mode.  But as in the shell,
using a less-than on a file never creates a new file; it only opens an
existing one.  On the other hand, using a greater-than always clobbers
(truncates to zero length) an existing file, or creates a brand-new one
if there isn't an old one.  Adding a "+" for read-write doesn't affect
whether it only works on existing files or always clobbers existing ones.

    open(my $wtmp, '+<', '/usr/adm/wtmp')
        or die "can't open /usr/adm/wtmp: $!";

    open(my $screen, '+>', 'lkscreen')
        or die "can't open lkscreen: $!";

    open(my $logfile, '+>>', '/var/log/applog')
        or die "can't open /var/log/applog: $!";

The first one won't create a new file, and the second one will always
clobber an old one.  The third one will create a new file if necessary
and not clobber an old one, and it will {*XXX* allow you to > lets WHO *XXX*} read at any point
in the file, but all writes will always go to the end.  In short,
the first case is substantially more common than the second and third
cases, which are almost always wrong.  (If you know C, the plus in
Perl's C<open> is historically derived from the one in C's fopen(3S),
which it ultimately calls.)

{*XXX* In fact > OMIT *XXX*}, when it comes to updating a file, unless you're working on
a binary file as in the WTMP case above, you probably don't want to
use this approach for updating.  Instead, Perl's B<-i> flag comes to
the rescue.  The following command takes {*XXX* all the > all, the, or OMIT *XXX*} C, C++, or yacc source
or header files and changes all their foo's to bar's, leaving
the old version in the original filename with a ".orig" tacked
on the end:

    $ perl -i.orig -pe 's/\bfoo\b/bar/g' *.[Cchy]

This is a short cut for some renaming games that are really
the best way to update textfiles.  See the second question in
L<perlfaq5> for more details.

=head2 Filters
{*XXX* One of the most > Do not use at the beginning of a paragraph, best to AVOID because of overuse *XXX*} common uses for C<open> is one you never
even notice.  When you process the ARGV filehandle using

C<< <ARGV> >>, Perl actually does an implicit open
on each file in @ARGV.  Thus a program called like this:

    $ myprogram file1 file2 file3

can have all its files opened and processed one at a time
using a {*XXX* construct > build *XXX*} no more complex than:

    while (<>) {
        # do something with $_
    }

If @ARGV is empty when the loop first begins, Perl pretends you've opened
up minus, that is, the standard input.  {*XXX* In fact > OMIT *XXX*}, $ARGV, the currently
open file during C<< <ARGV> >> processing, is even set to "-"
in these circumstances.

You are welcome to pre-process your @ARGV before starting the loop to
make sure it's to your liking.  One reason to do this might be to remove
command options beginning with a minus.  While you can always roll the
simple ones by hand, the Getopts modules are good for this:

    use Getopt::Std;

    # -v, -D, -o ARG, sets $opt_v, $opt_D, $opt_o
    getopts("vDo:");

    # -v, -D, -o ARG, sets $args{v}, $args{D}, $args{o}
    getopts("vDo:", \%args);

Or the standard Getopt::Long module to permit named arguments:

    use Getopt::Long;
    GetOptions( "verbose"  => \$verbose,        # --verbose
                "Debug"    => \$debug,          # --Debug
                "output=s" => \$output );
        # --output=somestring or --output somestring

Another reason for preprocessing arguments is to make an empty
argument list default to all files:

    @ARGV = glob("*") unless @ARGV;

You could even filter out all but plain, text files.  This is a bit
silent, {*XXX* of course > OMIT *XXX*}, and you might prefer to mention them on the way.

    @ARGV = grep { -f && -T } @ARGV;

If you're using the B<-n> or B<-p> command-line options, you
should put changes to @ARGV in a C<BEGIN{}> block.

Remember that a {*XXX* normal > (weak definition) *XXX*} C<open> has special properties, in that it might
call fopen(3S) or it might called popen(3S), depending on what its
argument looks like; that's why it's sometimes called "magic open".
Here's an example:

    $pwdinfo = `domainname` =~ /^(\(none\))?$/
                    ? '< /etc/passwd'
                    : 'ypcat passwd |';

    open(PWD, $pwdinfo)
        or die "can't open $pwdinfo: $!";

This sort of thing also comes into play in filter processing.  Because
C<< <ARGV> >> processing employs the {*XXX* normal > (weak definition) *XXX*}, shell-style Perl C<open>,
it respects {*XXX* all the > all, the, or OMIT *XXX*} special things we've already seen:

    $ myprogram f1 "cmd1|" - f2 "cmd2|" f3 < tmpfile

That program will read from the file F<f1>, the process F<cmd1>, standard
input (F<tmpfile> {*XXX* in this case > here *XXX*}), the F<f2> file, the F<cmd2> command,
and finally the F<f3> file.

Yes, this also means that if you have files named "-" (and so on) in
your directory, they won't be processed as literal files by C<open>.
You'll need to pass them as "./-", much as you would for the I<rm> program,
or you could use C<sysopen> as described below.

{*XXX* One of the > one, a *XXX*} more {*XXX* interesting > (avoid using "interesting" when introducing something; simply introduce it) *XXX*} applications is to change files of a certain
name into pipes.  For example, to autoprocess gzipped or compressed
files by decompressing them with I<gzip>:

    @ARGV = map { /\.(gz|Z)$/ ? "gzip -dc $_ |" : $_  } @ARGV;

Or, if you have the I<GET> program installed from LWP,
you can fetch URLs before processing them:

    @ARGV = map { m#^\w+://# ? "GET $_ |" : $_ } @ARGV;

It's not for nothing that this is called magic C<< <ARGV> >>.
Pretty nifty, eh?

=head1 Open E<agrave> la shell

Perl's C<open> function was designed to mimic the way command-line
redirection in the shell works.  Here are some basic examples
from the shell:

    $ myprogram file1 file2 file3
    $ myprogram    <  inputfile
    $ myprogram    >  outputfile
    $ myprogram    >> outputfile
    $ myprogram    |  otherprogram
    $ otherprogram |  myprogram

And here are some more advanced examples:

    $ otherprogram      | myprogram f1 - f2
    $ otherprogram 2>&1 | myprogram -
    $ myprogram     <&3
    $ myprogram     >&4

Programmers accustomed to constructs like those above can take comfort
in learning that Perl directly supports these familiar constructs using
virtually the same syntax as the shell.

=head1 Open E<agrave> la C

If you want the convenience of the shell, then Perl's C<open> is
{*XXX* definitely > OMIT *XXX*} the way to go.  On the other hand, if you want finer precision
than C's simplistic fopen(3S) provides you should look to Perl's
C<sysopen>, which is a direct hook into the open(2) system call.
That does mean it's a bit more involved, but that's the price of
precision.

C<sysopen> takes 3 (or 4) arguments.

    sysopen HANDLE, PATH, FLAGS, [MASK]

The HANDLE argument is a filehandle just as with C<open>.  The PATH is
a literal path, one that doesn't pay attention to any greater-thans or
less-thans or pipes or minuses, nor ignore whitespace.  If it's there,
it's part of the path.  The FLAGS argument contains one or more values
derived from the Fcntl module that have been or'd together using the
bitwise "|" operator.  The final argument, the MASK, is optional; if
present, it is combined with the user's current umask for the creation
mode of the file.  You should usually omit this.

Although the traditional values of read-only, write-only, and read-write
are 0, 1, and 2 respectively, this is known not to hold true on some
systems.  Instead, it's best to load in the appropriate constants first
from the Fcntl module, which supplies the following standard flags:

    O_RDONLY            Read only
    O_WRONLY            Write only
    O_RDWR              Read and write
    O_CREAT             Create the file if it doesn't exist
    O_EXCL              Fail if the file already exists
    O_APPEND            Append to the file
    O_TRUNC             Truncate the file
    O_NONBLOCK          Non-blocking access

Less common flags that are sometimes available on some operating
systems include C<O_BINARY>, C<O_TEXT>, C<O_SHLOCK>, C<O_EXLOCK>,
C<O_DEFER>, C<O_SYNC>, C<O_ASYNC>, C<O_DSYNC>, C<O_RSYNC>,
C<O_NOCTTY>, C<O_NDELAY> and C<O_LARGEFILE>.  Consult your open(2)
manpage or its local equivalent for details.
(Note: the C<O_LARGEFILE> flag, if available, is automatically added to
the sysopen() flags because large files are the default.)

Here's how to use C<sysopen> to emulate the simple C<open> calls we had
before.  We'll omit the C<or die $!> checks for clarity, but make sure
you always check the return values in real code.  These aren't quite
the same, since C<open> will trim leading and trailing whitespace,
but you'll get the idea.

To open a file for reading:

    open(my $fh, '<', $path);
    sysopen(my $fh, $path, O_RDONLY);

To open a file for writing, creating a new file if needed or else truncating
an old file:

    open(my $fh, '>', $path);
    sysopen(my $fh, $path, O_WRONLY | O_TRUNC | O_CREAT);

To open a file for appending, creating one if necessary:

    open(my $fh, '>>', $path);
    sysopen(my $fh, $path, O_WRONLY | O_APPEND | O_CREAT);

To open a file for update, where the file must already exist:

    open(my $fh, '+<', $path);
    sysopen(my $fh, $path, O_RDWR);

And here are things you can do with C<sysopen> that you cannot do with
a regular C<open>.  As you'll see, it's just a matter of controlling the
flags in the third argument.

To open a file for writing, creating a new file which must not previously
exist:

    sysopen(my $fh, $path, O_WRONLY | O_EXCL | O_CREAT);

To open a file for appending, where that file must already exist:

    sysopen(my $fh, $path, O_WRONLY | O_APPEND);

To open a file for update, creating a new file if necessary:

    sysopen(my $fh, $path, O_RDWR | O_CREAT);
To open a file for update, where that file must not already exist:

    sysopen(my $fh, $path, O_RDWR | O_EXCL | O_CREAT);

To open a file without blocking, creating one if necessary:

    sysopen(my $fh, $path, O_WRONLY | O_NONBLOCK | O_CREAT);

=head2 Permissions E<agrave> la mode

If you omit the MASK argument to C<sysopen>, Perl uses the octal value
0666.  The {*XXX* normal > (weak definition) *XXX*} MASK to use for executables and directories should
be 0777, and for anything else, 0666.

Why so permissive?  Well, it isn't really.  The MASK will be modified
by your process's current C<umask>.  A umask is a number representing
I<disabled> permissions bits; that is, bits that will not be turned on
in the created file's permissions field.

For example, if your C<umask> were 027, then the 020 part would
disable the group from writing, and the 007 part would disable others
from reading, writing, or executing.  Under these conditions, passing
C<sysopen> 0666 would create a file with mode 0640, since C<0666 & ~027>
is 0640.

You should seldom use the MASK argument to C<sysopen()>.  That takes
away the user's freedom to choose what permission new files will have.
Denying choice is almost always a bad thing.  One exception would be for
cases where sensitive or private {*XXX* data is > data are *XXX*} being stored, such as with mail
folders, cookie files, and internal temporary files.

=head1 Obscure Open Tricks

=head2 Re-Opening Files (dups)

Sometimes you already have a filehandle open, and want to make another
handle that's a duplicate of the first one.  In the shell, we place an
ampersand in front of a file descriptor number when doing redirections.
For example, C<< 2>&1 >> makes descriptor 2 (that's STDERR in Perl)
be redirected into descriptor 1 (which is usually Perl's STDOUT).
The same is {*XXX* essentially > OMIT *XXX*} true in Perl: a filename that begins with an
ampersand is treated instead as a file descriptor if a number, or as a
filehandle if a string.

    open(my $saveout,   '>&', $saverr)
        or die "couldn't dup saveerr: $!";
    open(my $mhcontext, '<&', '4')
        or die "couldn't dup fd4: $!";

That means that if a function is expecting a filename, but you don't
want to give it a filename because you already have the file open, you
can just pass the indirect filehandle.

    somefunction($logfile);

This way if somefunction() is planning on opening its argument, it can
just use the already opened handle.  This differs from passing a handle,
because with a handle, you don't open the file.  Here you have something
you can pass to open.

If you have one of those tricky, newfangled I/O objects that the C++
folks are raving about, then this doesn't work because those aren't a
proper filehandle in the native Perl sense.  You'll have to use fileno()
to pull out the proper descriptor number, assuming you can:

    use IO::Socket;
    my $handle = IO::Socket::INET->new("www.perl.com:80");
    my $fd = $handle->fileno;
    somefunction("&$fd"); # not an indirect function call

It can be easier (and {*XXX* certainly > (avoid if used to intensify any and every statement) *XXX*} will be faster) just to use real
filehandles though:

    use IO::Socket;
    local *REMOTE = IO::Socket::INET->new("www.perl.com:80");
    die "can't connect" unless defined(fileno(REMOTE));
    somefunction("&main::REMOTE");

If the filehandle or descriptor number is preceded not just with a simple
"&" but rather with a "&=" combination, then Perl will not create a
completely new descriptor opened to the same place using the dup(2)
system call.  Instead, it will just make something of an alias to the
existing one using the fdopen(3S) library call.  This is slightly more
parsimonious of systems resources, although this is less a concern
these days.  Here's an example of that:

    $fd = $ENV{"MHCONTEXTFD"};
    open(my $mhcontext, '<&=', $fd)
        or die "couldn't fdopen $fd: $!";

If you're using magic C<< <ARGV> >>, you could even pass in as a
command line argument in @ARGV something like C<"<&=$MHCONTEXTFD">,
but we've never seen anyone actually do this.

=head2 Dispelling the Dweomer

Perl is more of a DWIMmer language than something like Java--where DWIM
is an acronym for "do what I mean".  But this principle sometimes leads
to more hidden magic than one knows what to do with.  In this way, Perl
is also filled with I<dweomer>, an obscure word meaning an enchantment.
Sometimes, Perl's DWIMmer is just too much like dweomer for comfort.

If magic C<open> is a bit too magical for you, you don't have to turn
to C<sysopen>.  To open a file with arbitrary weird characters in
it, it's necessary to protect any leading and trailing whitespace.
Leading whitespace is protected by inserting a C<"./"> in front of a
filename that starts with whitespace.  Trailing whitespace is protected
by appending an ASCII NUL byte (C<"\0">) at the end of the string.

    $file =~ s#^(\s)#./$1#;
    open(my $fh, '<', "$file\0")
        or die "can't open $file: $!";

This assumes, {*XXX* of course > OMIT *XXX*}, that your system considers dot the current
working directory, slash the directory separator, and disallows ASCII
NULs within a valid filename.  Most systems follow these conventions,
including all POSIX systems as well as proprietary Microsoft systems.
The only vaguely popular system that doesn't work this way is the
"Classic" Macintosh system, which uses a colon where the rest of us
use a slash.  Maybe C<sysopen> isn't such a bad idea after all.

If you want to use C<< <ARGV> >> processing in a totally boring
and non-magical way, you could do this first:

    #   "Sam sat on the ground and put his head in his hands.
    #   'I wish I had never come here, and I don't want to see
    #   no more magic,' he said, and fell silent."
    for (@ARGV) {
        s#^([^./])#./$1#;
        $_ .= "\0";
    }
    while (<>) {
        # now process $_
    }

But be warned that users will not appreciate being unable to use "-"
to mean standard input, per the standard convention.

=head2 Paths as Opens

You've probably noticed how Perl's C<warn> and C<die> functions can
produce messages like:

    Some warning at scriptname line 29, <FH> line 7.

That's because you opened a filehandle FH, and had read in seven records
from it.  {*XXX* But what > that *XXX*} was the name of the file, rather than the handle?


If you aren't running with C<strict refs>, or if you've turned them off
temporarily, then all you have to do is this:

    open($path '<', $path)
        or die "can't open $path: $!";
    while (<$path>) {
        # whatever
    }

Since you're using the pathname of the file as its handle,
you'll get warnings more like

    Some warning at scriptname line 29, </etc/motd> line 7.

=head2 Single Argument Open

Remember how we said that Perl's open took two arguments?  That was a
passive prevarication.  You see, it can also take just one argument.
If and only if the variable is a global variable, not a lexical, you
can pass C<open> just one argument, the filehandle, and it will
get the path from the global scalar variable of the same name.

    $FILE = "/etc/motd";
    open FILE or die "can't open $FILE: $!";
    while (<FILE>) {
        # whatever
    }

Why is this here?  Someone has to cater to the hysterical porpoises.
It's something that's been in Perl since the very beginning, if not
before.

=head2 Playing with STDIN and STDOUT

One {*XXX* clever > (greatly overused word, restrict to small matters) *XXX*} move with STDOUT is to explicitly close it when you're done
with the program.

    END { close(STDOUT) or die "can't close stdout: $!" }

If you don't do this, and your program fills up the disk partition {*XXX* due
to > because, owing to *XXX*} a command line redirection, it won't report the error exit with a
failure status.

You don't have to accept the STDIN and STDOUT you were given.  You are
welcome to reopen them if you'd like.

    open(STDIN, '<', 'datafile')
        or die "can't open datafile: $!";

    open(STDOUT, '>', 'output')
        or die "can't open output: $!";

And then these can be accessed directly or passed on to subprocesses.
This makes it look as though the program were initially invoked
with those redirections from the command line.

It's probably more {*XXX* interesting > (avoid using "interesting" when introducing something; simply introduce it) *XXX*} to connect these to pipes.  For example:
    $pager = $ENV{PAGER} || "(less || more)";
    open(STDOUT, '|-', $pager)
        or die "can't fork a pager: $!";

This makes it appear as though your program were called with its STDOUT
already piped into your pager.  You can also use this kind of thing
{*XXX* in conjunction with > with *XXX*} an implicit fork to yourself.  You might do this
if you would rather handle the post processing in your own program,
just in a different process:

    head(100);
    while (<>) {
        print;
    }

    sub head {
        my $lines = shift || 20;
        return if $pid = open(STDOUT, "|-");       # return if parent
        die "cannot fork: $!"
            unless defined $pid;
        while (<STDIN>) {
            last
                if --$lines < 0;
            print;
        }
        exit;
    }

This technique can be applied to repeatedly push as many filters on your
output stream as you wish.

=head1 Other I/O Issues

These topics aren't really arguments related to C<open> or C<sysopen>,
but they do affect what you do with your open files.

=head2 Opening Non-File Files

When is a file not a file?  Well, you could say when it exists but
isn't a plain file.   We'll check whether it's a symbolic link first,
just {*XXX* in case > if *XXX*}.

    if (-l $file || ! -f _) {
        print "$file is not a plain file\n";
    }

What other kinds of files are there than, well, files?  Directories,
symbolic links, named pipes, Unix-domain sockets, and block and character
devices.  Those are all files, too--just not I<plain> files.  This isn't
the same issue as being a text file. Not all text files are plain files.
Not all plain files are text files.  That's why there are separate C<-f>
and C<-T> file tests.


To open a directory, you should use the C<opendir> function, then
process it with C<readdir>, carefully restoring the directory
name if necessary:

    opendir(my $dh, $dirname)
        or die "can't opendir $dirname: $!";
    while (defined($file = readdir($dh))) {
        # do something with "$dirname/$file"
    }
    closedir($dh);

If you want to process directories recursively, it's better to use the
File::Find module.  For example, this prints out all files recursively
and adds a slash to their names if the file is a directory.

    @ARGV = qw(.) unless @ARGV;
    use File::Find;
    find(sub { print $File::Find::name, -d && '/', "\n" }, @ARGV);

This finds all bogus symbolic links beneath a particular directory:

    find(sub { print "$File::Find::name\n" if -l && !-e }, $dir);

As you see, with symbolic links, you can just pretend that it is
what it points to.  Or, if you want to know I<what> it points to, then
C<readlink> is called for:

    if (-l $file) {
        if (defined($whither = readlink($file))) {
            print "$file points to $whither\n";
        } else {
            print "$file points nowhere: $!\n";
        }
    }

=head2 Opening Named Pipes

Named pipes are a different matter.  You pretend they're regular files,
but their opens will normally block until there is both a reader and
a writer.  You can read more about them in L<perlipc/"Named Pipes">.
Unix-domain sockets are rather different beasts as well; they're
described in L<perlipc/"Unix-Domain TCP Clients and Servers">.

When it comes to opening devices, it can be easy and it can be tricky.
We'll assume that if you're opening up a block device, you know what
you're doing.  The character devices are more {*XXX* interesting > (avoid using "interesting" when introducing something; simply introduce it) *XXX*}.  These are
typically {*XXX* used for > (used for [...] purposes: used for [...]) *XXX*} modems, mice, and some kinds of printers.  This is
described in L<perlfaq8/"How do I read and write the serial port?">
It's often enough to open them carefully:

    sysopen(my $ttyin, "/dev/ttyS1", O_RDWR | O_NDELAY | O_NOCTTY)
        # (O_NOCTTY no longer needed on POSIX systems)
        or die "can't open /dev/ttyS1: $!";
    open(my $ttyout, '+>&', $ttyin)
        or die "can't dup /dev/ttyS1: $!";

    $ofh = select($ttyout); $| = 1; select($ofh);

    print $ttyout "+++at\015";
    $answer = <$ttyin>;

With descriptors that you haven't opened using C<sysopen>, such as
sockets, you can set them to be non-blocking using C<fcntl>:

    use Fcntl;
    my $old_flags = fcntl($handle, F_GETFL, 0)
        or die "can't get flags: $!";
    fcntl($handle, F_SETFL, $old_flags | O_NONBLOCK)
        or die "can't set non blocking: $!";

Rather than losing yourself in a morass of twisting, turning C<ioctl>s,
all dissimilar, if you're going to manipulate ttys, it's best to
make calls out to the stty(1) program if you have it, or else use the
portable POSIX interface.  To figure this all out, you'll need to read the
termios(3) manpage, which describes the POSIX interface to tty devices,
and then L<POSIX>, which describes Perl's interface to POSIX.  There are
also some high-level modules on CPAN that can help you with these games.
Check out Term::ReadKey and Term::ReadLine.

=head2 Opening Sockets

What else can you open?  To open a connection using sockets, you won't use
one of Perl's two open functions.  See
L<perlipc/"Sockets: Client/Server Communication"> for that.  Here's an
example.  Once you have it, you can use $sock as a bidirectional filehandle.

    use IO::Socket;
    my $sock = IO::Socket::INET->new("www.perl.com:80");

For opening up a URL, the LWP modules from CPAN are just what
the doctor ordered.  There's no filehandle interface, but
it's still easy to get the contents of a document:

    use LWP::Simple;
    my $doc = get('http://www.cpan.org/');

=head2 Binary Files

On certain legacy systems with what could charitably be called terminally
convoluted (some would say broken) I/O models, a file isn't a file--at
least, not {*XXX* with respect to > about *XXX*} the C standard I/O library.  On these old
systems whose libraries (but not kernels) distinguish between text and
binary streams, to get files to behave properly you'll have to bend over
backwards to avoid nasty problems.  On such infelicitous systems, sockets
and pipes are already opened in binary mode, and there is currently no
way to turn that off.  With files, you have more options.

Another option is to use the C<binmode> function on the appropriate
handles before doing regular I/O on them:

    binmode(STDIN);
    binmode(STDOUT);
    while (<STDIN>) { print }

Passing C<sysopen> a non-standard flag option will also open the file in
binary mode on those systems that support it.  This is the equivalent of
opening the file normally, then calling C<binmode> on the handle.

    sysopen(my $bindat, "records.data", O_RDWR | O_BINARY)
        or die "can't open records.data: $!";

Now you can use C<read> and C<print> on that handle without worrying
about the non-standard system I/O library breaking your data.  It's not
a pretty picture, but then, legacy systems seldom are.  CP/M will be
with us until the end of days, and after.

On systems with exotic I/O systems, it turns out that, astonishingly
enough, even unbuffered I/O using C<sysread> and C<syswrite> might do
sneaky data mutilation behind your back.

    while (sysread($whence, $buf, 1024)) {
        syswrite($whither, $buf, length($buf));
    }

Depending on the vicissitudes of your runtime system, even these calls
may need C<binmode> or C<O_BINARY> first.  Systems known to be free of
such difficulties include Unix, the Mac OS, Plan 9, and Inferno.

=head2 File Locking

In a multitasking environment, you may need to be careful not to collide
with other processes who want to do I/O on the same files as you
are working on.  You'll often need shared or exclusive locks
on files for reading and writing respectively.  You might just
pretend that only exclusive locks exist.

Never use the existence of a file C<-e $file> as a locking indication,
because there is a race condition between the test for the existence of
the file and its creation.  It's possible for another process to create
a file in the slice of time between your existence check and your {*XXX* attempt
to > try to *XXX*} create the file.  Atomicity is critical.
Perl's most portable locking interface is via the C<flock> function,
whose simplicity is emulated on systems that don't directly support it
such as SysV or Windows.  The underlying semantics may affect how
it all works, so you should learn how C<flock> is implemented on your
system's port of Perl.

File locking I<does not> lock out another process that would like to
do I/O.  A file lock only locks out others trying to get a lock, not
processes trying to do I/O.  Because locks are advisory, if one process
uses locking and another doesn't, all bets are off.

By default, the C<flock> call will block until a lock is granted.
A request for a shared lock will be granted {*XXX* as soon as > when, once *XXX*} there is no
exclusive locker.  A request for an exclusive lock will be granted {*XXX* as
soon as > when, once *XXX*} there is no locker of any kind.  Locks are on file descriptors,
not file names.  You can't lock a file until you open it, and you can't
hold on to a lock once the file has been closed.

Here's how to get a blocking shared lock on a file, typically {*XXX* used
for > (used for [...] purposes: used for [...]) *XXX*} reading:

    use 5.004;
    use Fcntl qw(:DEFAULT :flock);
    open(my $fh, '<', 'filename')
        or die "can't open filename: $!";
    flock($fh, LOCK_SH)
        or die "can't lock filename: $!";
    # now read from $fh

You can get a non-blocking lock by using C<LOCK_NB>.

    flock(my $fh, LOCK_SH | LOCK_NB)
        or die "can't lock filename: $!";

This can be useful for producing more user-friendly behaviour by warning
if you're going to be blocking:

    use 5.004;
    use Fcntl qw(:DEFAULT :flock);
    open(my $fh, '<', 'filename')
        or die "can't open filename: $!";
    unless (flock($fh, LOCK_SH | LOCK_NB)) {
        $| = 1;
        print "Waiting for lock...";
        flock($fh, LOCK_SH)
            or die "can't lock filename: $!";
        print "got it.\n"
    }
    # now read from $fh

To get an exclusive lock, typically {*XXX* used for > (used for [...] purposes: used for [...]) *XXX*} writing, you have to be
careful.  We C<sysopen> the file so it can be locked before it gets
emptied.  You can get a nonblocking version using C<LOCK_EX | LOCK_NB>.

    use 5.004;
    use Fcntl qw(:DEFAULT :flock);
    sysopen(my $fh, "filename", O_WRONLY | O_CREAT)
        or die "can't open filename: $!";
    flock($fh, LOCK_EX)
        or die "can't lock filename: $!";
    truncate($fh, 0)
        or die "can't truncate filename: $!";
    # now write to $fh

Finally, {*XXX* due to > because, owing to *XXX*} the uncounted millions who cannot be dissuaded from
wasting cycles on useless vanity devices called hit counters, here's
how to increment a number in a file safely:

    use Fcntl qw(:DEFAULT :flock);

    sysopen(my $fh, 'numfile', O_RDWR | O_CREAT)
        or die "can't open numfile: $!";
    # autoflush $fh
    $ofh = select($fh); $| = 1; select ($ofh);
    flock($fh, LOCK_EX)
        or die "can't write-lock numfile: $!";

    $num = <$fh> || 0;
    seek($fh, 0, 0)
        or die "can't rewind numfile : $!";
    print $fh $num+1, "\n"
        or die "can't write numfile: $!";

    truncate($fh, tell($fh))
        or die "can't truncate numfile: $!";
    close($fh)
        or die "can't close numfile: $!";

=head2 IO Layers

In Perl 5.8.0 a new I/O framework called "PerlIO" was introduced.
This is a new "plumbing" for {*XXX* all the > all, the, or OMIT *XXX*} I/O happening in Perl; for the
most part everything will work just as it did, but PerlIO also brought
in some new features such as the ability to think of I/O as "layers".
One I/O layer may in addition to just moving the data also do
transformations on the data.  Such transformations may include
compression and decompression, encryption and decryption, and transforming
between various character encodings.

Full discussion about the features of PerlIO is out of scope for this
tutorial, but here is how to recognize the layers being used:

=over 4

=item *

The three-(or more)-argument form of C<open> is being used and the
second argument contains something else in addition to the usual
C<< '<' >>, C<< '>' >>, C<< '>>' >>, C<< '|' >> and their variants,
for example:

    open(my $fh, "<:crlf", $fn);

=item *

The two-argument form of C<binmode> is being used, for example

    binmode($fh, ":encoding(utf16)");

=back

For more detailed discussion about PerlIO see L<PerlIO>;
for more detailed discussion about Unicode and I/O see L<perluniintro>.

=head1 SEE ALSO

The C<open> and C<sysopen> functions in perlfunc(1);
the system open(2), dup(2), fopen(3), and fdopen(3) manpages;
the POSIX documentation.

=head1 AUTHOR and COPYRIGHT

Copyright 1998 Tom Christiansen.

This documentation is free; you can redistribute it {*XXX* and/or > AVOID *XXX*} modify it
under the same terms as Perl itself.

Irrespective of its distribution, all code examples in these files are
hereby placed into the public domain.  You are permitted and
encouraged to use this code in your own programs for fun or for profit
as you see fit.  A simple comment in the code giving credit would be
courteous but is not required.

=head1 HISTORY

First release: Sat Jan  9 08:09:11 MST 1999
Congratulations, you destroyed my motivation with blaming me for text that was written by someone else probably a decade ago!
English is not my first language, finding a better wording is the goal or this review.
I spent hours on #p5p discussing things like or vs. || and the outcome of my changes was always based on that.
In this case 'or' is preferred because it enables to use the paren-less version of open.
As I don't have commit rights to the branch Dave put my work with his corrections feel free to do whatever you like with it!
My goal was to just replace 2-arg open with 3-arg open and 'direct' with 'indirect' filehandles as a first step to make sure at least that gets into 5.18.
The WIP of the complete rewrite is on github: https://github.com/abraxxa/perlopentut/blob/master/perlopentut.pod.

demerphq

unread,
Feb 16, 2013, 4:35:54 AM2/16/13
to Tom Christiansen, Alexander Hartmaier, Ricardo Signes, Perl5 Porters Mailing List
On 16 February 2013 06:36, Alexander Hartmaier <alex.ha...@gmail.com> wrote:
> On Thu, Feb 14, 2013 at 9:12 PM, Tom Christiansen <bould...@yahoo.com>
> wrote:
>>
>> The very least you could do is run it through diction.
>>
>> Almost all of the 78 of the XXX's below need fixing.
>>
>> --tom
[snip]
>> First release: Sat Jan 9 08:09:11 MST 1999
>
> Congratulations, you destroyed my motivation with blaming me for text that
> was written by someone else probably a decade ago!

Nice one Tom! It looks like you accomplished your goal of ensuring
that your pretty little document is not changed at all. Too bad the
collateral damage is a new contributor to Perl being chased off.

Alexander: if you can be brought to still care, please, don't let Tom
piss you off, and please don't let him chase you away. We need people
like you. Don't let him get you down.

Yves

--
perl -Mre=debug -e "/just|another|perl|hacker/"

H.Merijn Brand

unread,
Feb 16, 2013, 5:05:16 AM2/16/13
to perl5-...@perl.org
On Sat, 16 Feb 2013 06:36:20 +0100, Alexander Hartmaier
<alex.ha...@gmail.com> wrote:

> Congratulations, you destroyed my motivation with blaming me for text
> that was written by someone else probably a decade ago!

By Tom :)

Alexander, PLEASE continue!
There is quite a bunch of people here that support your effort,some in
silence as they do not dare to stand up to Tom's (always overwhelming)
use of native verbosity in English.

--
H.Merijn Brand http://tux.nl Perl Monger http://amsterdam.pm.org/
using perl5.00307 .. 5.17 porting perl5 on HP-UX, AIX, and openSUSE
http://mirrors.develooper.com/hpux/ http://www.test-smoke.org/
http://qa.perl.org http://www.goldmark.org/jeff/stupid-disclaimers/

demerphq

unread,
Feb 16, 2013, 6:04:15 AM2/16/13
to H.Merijn Brand, perl5-...@perl.org
On 16 February 2013 11:05, H.Merijn Brand <h.m....@xs4all.nl> wrote:
> On Sat, 16 Feb 2013 06:36:20 +0100, Alexander Hartmaier
> <alex.ha...@gmail.com> wrote:
>
>> Congratulations, you destroyed my motivation with blaming me for text
>> that was written by someone else probably a decade ago!
>
> By Tom :)

Yeah, and he doesn't have the decency to send a patch for his own fixes.

In fact, I don't remember ever seeing him submit a patch. Maybe he
has, but I don't remember it.

> Alexander, PLEASE continue!

Indeed.

> There is quite a bunch of people here that support your effort,some in
> silence as they do not dare to stand up to Tom's (always overwhelming)
> use of native verbosity in English.

His verbosity is overwhelming even for native speakers.

Tom Christiansen

unread,
Feb 16, 2013, 9:11:45 AM2/16/13
to demerphq, Tom Christiansen, Alexander Hartmaier, Ricardo Signes, Perl5 Porters Mailing List
A lot of us use parens around are function arguments. Please
do not make Perl -- or its documenation -- tell us we should
not do that.

--tom

demerphq

unread,
Feb 16, 2013, 9:27:40 AM2/16/13
to Tom Christiansen, Tom Christiansen, Alexander Hartmaier, Ricardo Signes, Perl5 Porters Mailing List
Well, I don't think I said it should. I said that the documentation
should match the style most users will encounter in their Perl
development career, which as far as I have seen in modules, etc, is
not to use parens on open, and to use low precedence operators for
flow control.

Perl being packaged with loads of examples following outdated or
unusual style preferences just confuses people when they are at the
development phase where they are least able to deal with them.

Tom Christiansen

unread,
Feb 16, 2013, 10:25:17 AM2/16/13
to demerphq, Tom Christiansen, Alexander Hartmaier, Ricardo Signes, Perl5 Porters Mailing List
Using parentheses is *OUTDATED OR UNUSUAL STYLE PREFERENCES*?

Please.

Let's just run the documentation through perlcritic and perltidy with
per-user custom settings. Seems to me that's what most of this game
of musical chairs is really all about anyway, so let's cut to the chase.

--tom

Tom Christiansen

unread,
Feb 16, 2013, 10:47:19 AM2/16/13
to Ricardo Signes, Alexander Hartmaier, Tom Christiansen, Perl5 Porters Mailing List
The very first thing I would do is mine Programming Perl
for examples of open() and sysopen().

Yes, some are from more advanced topics that are beyond the scope
of a tutorial on open, but all are clearly examples that people
will be seeing a lot of.

--tom

1 4 open(GRADES, "<:utf8", "grades") || die "Can't open grades: $!\n";
2 open(SESAME, "filename") # read from existing file
3 open(SESAME, "< filename") # (same thing, explicitly)
4 open(SESAME, "> filename") # create file and write to it
5 open(SESAME, ">> filename") # append to existing file
6 open(SESAME, "| output-pipe-command") # set up an output filter
7 open(SESAME, "input-pipe-command |") # set up an input filter
8 open(SESAME, "<", $somefile) # read from existing file
9 open(SESAME, ">", $somefile) # create file and write to it
10 open(SESAME, ">>", $somefile) # append to existing file
11 open(SESAME, "|-", "output-pipe-command") # set up an output filter
12 open(SESAME, "-|", "input-pipe-command") # set up an input filter
13 open(SESAME, "< :encoding(UTF-8)", $somefile)
14 open(SESAME, "> :crlf", $somefile)
15 open(SESAME, ">> :encoding(MacRoman)", $somefile)
16 open(my $handle, "< :crlf :encoding(cp1252)", $somefile)
17 || die "can't open $somefile: $!";
18 open(GRADES, "<:utf8", "grades") || die "Can't open file grades: $!\n";
19 open(FH, "<", $path) || return undef;
20 open(my $fh, "<", $path) || return undef;
21 if (!open(ARGV, "<", $ARGV)) {
22 warn "Can't open $ARGV: $!\n";
23 open(my $fh, "<", "data.txt");
24 open(FILE, "<", "somefile") || die "Can't open somefile: $!\n";
25 unless (open(FOO, "<", $foo)) { die "Can't open $foo: $!" }
26 if (!open(FOO, "<", $foo)) { die "Can't open $foo: $!" }
27 die "Can't open $foo: $!" unless open(FOO, "<", $foo);
28 die "Can't open $foo: $!" if !open(FOO, "<", $foo);
29 open(FOO, "<", $foo) || die "Can't open $foo: $!";
30 open(FOO, "<", $foo) or die "Can't open $foo: $!";
31 open(FOO, "<", $file) or die "open $file: $!";
32 open FILE, "<", $file
33 or warn "Can't open $file: $!\n", next FILE; # WRONG
34 open FILE, "<", $file
35 or warn("Can't open $file: $!\n"), next FILE; # okay
36 unless (open FILE, "<", $file) {
37 warn "Can't open $file: $!\n";
38 open(my $fh, ">", "/dev/passwd") or ...; # WRONG
39 open(DICT, "/usr/dict/words") or die "Can't open words: $!\n";
40 open(*FH, $file) || next;
41 open($fh, $file) || next;
42 open(FILE, "> tvinfo.perldata") || die "can't open tvinfo: $!";
43 open(FILE, "< tvinfo.perldata") || die "can't open tvinfo: $!";
44 return unless open my $fh, $$self;
45 open(my $fh, ">", $$self) || croak "can't clobber $$self: $!";
46 open(F, "> $file") || croak "can't open $file: $!";
47 open(my $self, $form, @_) || croak "can't open $form@_: $!";
48 open($self, $form, @_) || croak "can't reopen $name: $!";
49 open(FOO, "+<", "filename"); # Reopen FOO, calling OPEN.
50 open(my $self, $form, @_) || croak "can't open $name: $!";
51 open($self, $form, @_) || croak "can't reopen $name: $!";
52 open(my $fh, ">$path") || die "can't write $path";
53 open(my $handle, ">", $filename)
54 || die "Cannot open $filename: $!\n";
55 open(OUTPUT, "> :raw :encoding(UTF-16LE) :crlf", $filename)
56 or die "can't open $filename: $!";
57 open(FH, "< filename") || die "can't open filename: $!";
58 open(FH, "< filename") || die "can't open filename: $!";
59 sysopen(FH, "filename", O_WRONLY | O_CREAT)
60 || die "can't open filename: $!";
61 sysopen(FH, "counterfile", O_RDWR | O_CREAT)
62 || die "can't open counterfile: $!";
63 sysopen(DBLOCK, $LCK, O_RDONLY | O_CREAT)
64 || die "can't open $LCK: $!";
65 open(INPUT, "< /etc/motd") || die "/etc/motd: $!";
66 open(INPUT, "< /etc/motd") || die "/etc/motd: $!";
67 open(STDIN, "<&INPUT") || die "dup: $!";
68 open(INPUT, "< /etc/motd") || die "/etc/motd: $!";
69 open(INPUT, "<&=$ENV{input_fdno}")
70 open SPOOLER, "| cat -v | lpr -h 2>/dev/null"
71 open SPOOLER, "|-", "lpr", "-h" # requires 5.6.1
72 open(STDOUT, "| $pager") || die "can't fork a pager: $!";
73 open STATUS, "netstat -an 2>/dev/null |"
74 open STATUS, "-|", "netstat", "-an" # requires 5.6.1
75 if (open(TO, "|-")) {
76 if (open(FROM, "-|")) {
77 my $fh; # open will fill this in
78 unless (open($fh, ">", $path)) {
79 return if my $pid = open(STDOUT, "|-");
80 my $kidpid = open my $self, "-|";
81 open(PROG_TO_READ_AND_WRITE, "| some program |") # WRONG!
82 sysopen(FIFO, $fpath, O_WRONLY)
83 open(STDIN, "<&", $client) || die "can't dup client: $!";
84 open(STDOUT, ">&", $client) || die "can't dup client: $!";
85 open(STDERR, ">&", $client) || die "can't dup client: $!";
86 open(ARGVOUT, ">$ARGV");
87 open(OOF, "< $arg"); # OK (read-only opens not checked).
88 open(OOF, "> $arg"); # Insecure (trying to write to tainted arg).
89 open(OOF, "echo $arg|") # Insecure due to tainted $arg, but...
90 open(OOF,"-|") # Considered OK: see below for taint
91 open(OOF,"-|", "echo", $arg) # Same as previous, likewise OKish.
92 # Magic open—could be anything
93 open(FH, $file) || die "can't magic open $file: $!";
94 # Guaranteed to be a read-only file open and not a pipe
95 open(FH, "< $file") || die "can't open $file: $!";
96 # WYSIWYG open: disables all convenience features.
97 open(FH, "<", $file) || die "can't open $file: $!";
98 sysopen(FH, $file, O_RDONLY) || die "can't sysopen $file: $!";
99 die "Can't fork open: $!" unless defined($pid = open(FROMKID, "-|"));
100 open(FROMKID, "-|") || exec("myprog", "arg1", "arg2")
101 open(FROMKID, "-|", "myprog", "arg1", "arg2");
102 defined ($pid = open(SAFE_WRITER, "|-"))
103 open(FH, "> /some/file/path")
104 || die "can't open /some/file/path for writing: $!";
105 open(FH, "<", $file)
106 || die "can't open $file for reading: $!";
107 open(FH, ">", $file)
108 || die "can't open $file for writing: $!";
109 open(FH, "<", $file)
110 || sysopen(FH, $file, O_WRONLY | O_CREAT | O_EXCL)
111 open(TMP, "> /tmp/foo.$$")
112 || die "can't open /tmp/foo.$$: $!";
113 } until sysopen(TMP, $name, O_RDWR | O_CREAT | O_EXCL, 0600);
114 open(FILE, $file) || die "$0: Can’t open $file for reading: $!\n";
115 open(FOO,$foo) || die "Can’t open $foo: $!";
116 die "Can’t open $foo: $!" unless open(FOO,$foo);
117 open(FILE, $existing_file) || die $!; # wrongish
118 open(FILE, "<$existing_file") || die $!; # righter
119 open(FILE, "<", $existing_file) || die $!; # righterer
120 open(MAIL, "|/usr/lib/sendmail -t") || die "cannot fork sendmail: $!";
121 open spellbook, study, read (scan, select, tell us);
122 open yourself, wide;
123 keys (hidden), open (locks, doors), tell secrets;
124 open(FILE, $myfile);
125 open(GIF, "< :raw", "vim-power.gif")
126 || die "Can't open vim-power.gif: $!";
127 open(OUTPUT, "| sort -rn | lpr -p") # pipe to sort and lpr
128 open(NETSTAT, "netstat -rn |")
129 open(STDIN, "<&NETSTAT")
130 || die "Can't open netscape history file: $!";
131 open(MBOX, ">> /usr/spool/mail/$ENV{USER}")
132 || die "can't open mailbox: $!";
133 open(MAILRC, "~/.mailrc") # WRONG: tilde is a shell thing
134 || die "can't open ~/.mailrc: $!";
135 open(MAILRC, <~/.mailrc>) # expand tilde first
136 || die "can't open ~/.mailrc: $!";
137 open(MAILRC, (glob("~/.mailrc"))[0]) # same, but more careful of list return
138 || die "can't open ~/.mailrc: $!";
139 open(my $tmp, "+>", undef) or die ...
140 open(my $fh, "< :encoding(UTF-8)", "filename")
141 || die "can't open UTF-8 encoded filename: $!";
142 open($fh, ">", \$variable) || ...
143 open(STDOUT, ">", \$variable) || die "can't memopen STDOUT: $!";
144 open(LOG, "> logfile") or die "Can't create logfile: $!"; # ok
145 open(LOG, ">", "logfile") or die "Can't create logfile: $!"; # better
146 open LOG, ">", "logfile" || die "Can't create logfile: $!"; # WRONG
147 open LOG, ">", "logfile" or die "Can't create logfile: $!"; # ok
148 open(LOG, ">", "logfile") or die "Can't create logfile: $!"; # good
149 open(LOG, ">", "logfile") || die "Can't create logfile: $!"; # good
150 open LOG, ">", "logfile"
151 open $fh, ">", "logfile" # $fh is autovivified
152 open(my $fh, ">", "logfile") || die ...
153 open(LOG, ">", "logfile") || die "Can't create logfile: $!";
154 open(INFO, "datafile") || die("can't open datafile: $!");
155 open(INFO, "< datafile") || die("can't open datafile: $!");
156 open(RESULTS, "> runstats") || die("can't open runstats: $!");
157 open(LOG, ">> logfile ") || die("can't open logfile: $!");
158 open(INFO, "datafile") or die "can't open datafile: $!";
159 open(INFO, "< datafile") or die "can't open datafile: $!";
160 open(RESULTS, "> runstats") or die "can't open runstats: $!";
161 open(LOG, ">> logfile ") or die "can't open logfile: $!";
162 open(INPUT, "-" ) || die; # re-open standard input for reading
163 open(INPUT, "<-") || die; # same thing, but explicit
164 open(OUTPUT, ">-") || die; # re-open standard output for writing
165 open(DBASE, "+< database")
166 || die "can't open existing database in update mode: $!";
167 open(PRINTER, "| lpr -Plp1") || die "can't fork: $!";
168 open(NET, "netstat -i -n |") || die "can't fork: $!";
169 open(PRINTER, "|-", "lpr -Plp1") || die "can't fork: $!";
170 open(NET, "-|", "netstat -i -n") || die "can't fork: $!";
171 open(PRINTER, "|-", "lpr", "-Plp1") || die "can't fork: $!";
172 open(NET, "-|", "netstat", "-i", "-n") || die "can't fork: $!";
173 my $pid = open(FROM_CHILD, "-|") // die "can't fork: $!";
174 open(FH, "| tr 'a-z' 'A-Z'"); # pipe to shell command
175 open(FH, "|-", "tr", "a-z", "A-Z" ); # pipe to bare command
176 open(FH, "|-") || exec("tr", "a-z", "A-Z") || die; # pipe to child
177 open(FOO, "|-", "tr", "a-z", "A-Z") || die; # pipe to child
178 open(FH, "cat -n 'file' |"); # pipe from shell command
179 open(FH, "-|", "cat", "-n", "file"); # pipe from bare command
180 open(FH, "-|") || exec("cat", "-n", "file") || die; # pipe from child
181 open(FOO, "-|", "cat", "-n", $file) || die; # pipe from child
182 open(FH, $filename) || die "Can't open $filename: $!";
183 open(SAVEOUT, ">&SAVEERR") || die "couldn't dup SAVEERR: $!";
184 open(MHCONTEXT, "<&4") || die "couldn't dup fd4: $!";
185 open SAVEOUT, ">&STDOUT";
186 open SAVEERR, ">&STDERR";
187 open(STDOUT, "> foo.out") || die "Can't redirect stdout";
188 open(STDERR, ">&STDOUT") || die "Can't dup stdout";
189 open STDOUT, ">&SAVEOUT";
190 open STDERR, ">&SAVEERR";
191 open(MHCONTEXT, "<&=$fdnum")
192 open(FH, "< $path\0") || die "can't open $path: $!";
193 open(FH, "<", $path) || die "can't open $path: $!";
194 sysopen(FH, $path, O_RDONLY) || die "can't open $path: $!";
195 open(FH, "< :raw", $path) || die "can't open $path: $!";
196 open(HANDLE, "> :encoding(UTF-16)", $file)
197 || die "Can't open $file: $!";
198 open(my $fh, "<&=STDIN") || die;
199 open(PASSWD, "/etc/passwd");
200 open(FH, "<", $path);
201 sysopen(FH, $path, O_RDONLY);
202 open(FH, ">", $path);
203 sysopen(FH, $path, O_WRONLY | O_TRUNC | O_CREAT);
204 open(FH, ">>", $path);
205 sysopen(FH, $path, O_WRONLY | O_APPEND | O_CREAT);
206 open(FH, "+<", $path);
207 sysopen(FH, $path, O_RDWR);
208 sysopen(FH, $path, O_WRONLY | O_EXCL | O_CREAT);
209 sysopen(FH, $path, O_WRONLY | O_APPEND);
210 sysopen(FH, $path, O_RDWR | O_CREAT);
211 sysopen(FH, $path, O_RDWR | O_EXCL | O_CREAT);
212 sysopen(FH, $path, O_WRONLY | O_NONBLOCK);
213 || die "Can't open aliases: $!";
214 open(my $fh, "<", $File::Find::name)
215 || die "can't open $File::Find::name: $!";
216 open my $fh, "<:encoding(UTF-8)", $filename;

Lukas Mai

unread,
Feb 16, 2013, 11:36:12 AM2/16/13
to Tom Christiansen, Perl5 Porters Mailing List, tch...@perl.com, Ricardo Signes
On 15.02.2013 16:43, Tom Christiansen wrote:
> On Fri, 2/15/13, Rob Dixon <rob....@gmx.com> wrote, quoting me:
>
>>> Also, implicit close is a bug, not a feature. Don't push it.
>>> Don't use it. Always check the close.
>
>> I wonder if you could explain your reasons for this Tom? Are
>> you saying that the proper way to code is with an explicit
>> close on every file handle? My instinct is to disagree with
>> you, but I woukld like to hear what you have to say first.
>
> Every single system call whose formal definition includes the possibility
> of failing and setting errno must always be checked for that condition.
> No exceptions. Just do it. I'm serious. Please read Darwin and Collyer
> in marwww.in2p3.fr/~hoffmann/pv/canthappen.pdf

I disagree with you.

[snip example of failing open/dup calls]


>> What would be a better alternative to closing and releasing a file
>> handle that is garbage-collected?
>
> Implicit, unchecked close is a bug.

No, it's not.


>> I would have thought the way things are, especially with
>> read-only file handles, was the best solution.
>
> No, that simply is wrong. What you are saying is that you do not
> care whether the read operation actually did what it was asked to
> do. That is a dangerously cavalier attitude that can and does lead
> to serious bugs. The read-only open(2) of slash above in init(8)
> was not checked for error, nor were the dup(2) syscalls.

No, I'm saying I don't care whether close did what it was asked to do,
not read. close is not read/open/dup.


> Let's say you read from one file and do something based on what
> you read from that file. Don't you think it matters when you
> read the first ten out of a hundred lines and then got an I/O
> error? The error bit will be set on the handle, just as it is
> when you have a buffer-delayed fflush(3)/fpurge(3) on the FILE*.
> These happen on reads, too. Either way it means means when you
> get around to the fclose(3), it will return the obligate error.

That is an interesting theory. I see nothing in 'man fread' or 'man
fclose' that indicates it works that way (where "that way" means: fclose
fails if the filehandle has the error bit set from a previous read
operation).

Besides, this is irrelevant. We're talking about Perl, not C. Perl
doesn't use stdio, and there's nothing in 'perldoc -f readline' or
'perldoc -f close' that says it works this way either.

From perldoc -f close:
| Closes the file or pipe associated with the filehandle, flushes the
| IO buffers, and closes the system file descriptor. Returns true if
| those operations succeed and if no error was reported by any PerlIO
| layer.

For a read-only handle, there are no buffers to flush. The only thing
that could fail is the underlying close(2). 'man 2 close' says:

| Not checking the return value of close() is a common but nevertheless
| serious programming error.

But then goes on:

| It is quite possible that errors on a previous write(2) operation are
| first reported at the final close(). Not checking the return value
| when closing the file may lead to silent loss of data.

So again, nothing about read-only file descriptors here.


> You *have* to check for that. The only exception is when you do
> not care about correctness. If that is the case, then don't run
> the program at all, since its "it ran correctly" status does not
> appear to concern you.

If close fails to close a read-only filehandle, there's nothing I can
do, so why bother?

> Consult your local read(2) and close(2) manpages. You will find
> many, many, many possible scenarios for those system calls to fail.
> Just on the read(2) alone, you will find things like this:

[snip]

> Those matter. If a system call *can* fail, you *must* check for that failure
> each and every single possible time. It does not matter whether the handle is
> read-only. Check its close. Always. No matter what.

Those are all about read(2), not close(2). Perl close doesn't call
read(2), so this is irrelevant. I don't see why it should not matter
whether the handle is read-only: All the errors described in the
documentation for fclose/close relate to buffering of unwritten data.
Read-only handles don't buffer unwritten data.

> Implicit close is a bug, not a feature.

You keep repeating that but I don't believe it.


--
Lukas Mai <l....@web.de>

Robert Sedlacek

unread,
Feb 16, 2013, 12:16:35 PM2/16/13
to perl5-...@perl.org
On Sat, 2013-02-16 at 08:25 -0700, Tom Christiansen wrote:
> Using parentheses is *OUTDATED OR UNUSUAL STYLE PREFERENCES*?

Yes. It is by many. The parenthesis version is more noisy. The word 'or'
is easier to parse than '||'. We are talking about people new to Perl
here. So I do indeed think that people not having to translate '||' to
'or' in their heads in a document with many examples is a good thing.

> Let's just run the documentation through perlcritic and perltidy with
> per-user custom settings. Seems to me that's what most of this game
> of musical chairs is really all about anyway, so let's cut to the chase.

It really is not.

regards,
--
Robert 'phaylon' Sedlacek

Perl 5 Consultant for
Shadowcat Systems Limited - http://shadowcat.co.uk/

Lukas Mai

unread,
Feb 16, 2013, 1:29:10 PM2/16/13
to Tom Christiansen, Tom Christiansen, Perl5 Porters Mailing List
On 16.02.2013 16:47, Tom Christiansen wrote:
> The very first thing I would do is mine Programming Perl
> for examples of open() and sysopen().

Why?

> Yes, some are from more advanced topics that are beyond the scope
> of a tutorial on open, but all are clearly examples that people
> will be seeing a lot of.

"clearly"? That doesn't follow. Most Perl code is not part of
Programming Perl, and understanding the code in Programming Perl is not
what perlopentut is for.

I think most of the code below is bad or at least outdated style,
particularly 2-arg open, bareword filehandles, and || in void context.

2-arg open is bad because it only works in some situations and doesn't
generalize well: You have to know that your filename doesn't start/end
with "weird" symbols (whitespace, >, <, |, etc.), which basically limits
you to (some) string literals, and it sets a bad precedent of interface
design, i.e. it munges two separate pieces of information (name of the
file, open mode) into one string that the callee has to parse out again.
3-arg open has none of these problems.

Bareword filehandles are bad because they only work in some situations
and don't generalize well: You're using a global name, so you have to
make sure no one else (in the same package) is using the same name at
the same time, otherwise you will get not an error but weird runtime
behavior. Any function doing this is automatically non-reentrant; worse,
all functions using the same bareword filehandle become part of a
mutually non-reentrant set. Bareword filehandles aren't first-class
objects, so storing them in data structures or passing them from/to
functions requires entirely new syntax (\*FOO or *FOO{IO} to pass them
in, then a scalar variable to refer to them).
Lexical filehandles have none of these problems.

'||' in void context is more of a personal style issue. I generally use
'or' in void context and '||' elsewhere to show whether I'm more
interested in the boolean result or the control flow side effects. This
also works well with the very low precedence of 'or'.
As for 'open' in particular, I've seen too many instances of the 'open
my $fh, "<", $file || die' bug to even show an example of 'open' and
'||' in the same statement, especially in a tutorial.
--
Lukas Mai <l....@web.de>

H.Merijn Brand

unread,
Feb 16, 2013, 2:00:03 PM2/16/13
to perl5-...@perl.org
On Sat, 16 Feb 2013 08:25:17 -0700, Tom Christiansen <tch...@perl.com>
wrote:

> Let's just run the documentation through perlcritic and perltidy with
> per-user custom settings. Seems to me that's what most of this game
> of musical chairs is really all about anyway, so let's cut to the chase.

I have been thinking, and I think I vented the idea once, to enable
embedded code chunks in documentation (primarily from (.pm) modules,
to be run *optionally* through perltidy (not perlcritic), so each and
every reader will read it in the way he or she *currently* is
programming and will understand the code instantly. My style is not
yours or Marks or Larrys or whoevers. We all have our issues and
preferences, be it parens, spaces, indents or (unneeded) empty lines
between code chunks.

The main thing for a coder or coding team is to be consistent and think
about the code styles being used in the team * for whatever reasons *

I have even gone through some of the modules we use on a daily basis
and read the on-line manual (using perldoc) to make all examples match
the style our company uses and have them local. That is some work, but
is enormously appreciated.


FWIW I recently sent a patch to a module owner with near to 20
syntactical fixes to the examples, just because I found those errors
during the restyling. I sent the patches in the authors style (however
awful *I* might have found that).

I would *LOVE* to see an option to perldoc that enables perltidy on
code snippets.

> --tom

Tom Christiansen

unread,
Feb 16, 2013, 3:09:55 PM2/16/13
to Lukas Mai, Tom Christiansen, Perl5 Porters Mailing List, Ricardo Signes
>I disagree with you.

I believe that errors should not go undetected;
is that what you are diagreeing with?

>> Implicit, unchecked close is a bug.

> No, it's not.

How long do you want to go back and forth on this? You think the
close *should* be ok hwne there are pending errors on the handle?

How do you explain the pipe-close issue then? If the wait status
is non-zero, the close is deemeed to fail. Certainly sounds like
close is where you ought to do the error handling. Nothing else
is reliable.

None of this matters anyway, because I have now successfully proven
to myself that Perl does not provide for reliable detection of I/O
errors at all, all documentation to the contrary. In particular,
this is either a lie or a bug:

$io->error

Returns a true value if the given handle has experienced any errors
since it was opened or since the last call to "clearerr", or if the
handle is invalid. It only returns false for a valid handle with no
outstanding errors.

No, it does not, given than I can both:

(1) read from a handle, get undef back with errno set, and yet that
handle's error() method returns false.

(1) write to a handle, get undef back with errno set, and yet that
handle's error() method returns false.

If you are correct and I cannot be expected to trust the return value of
close() to reflect pending I/O errors -- which I think is a design blunder
if so -- then I had *certainly* better be able to trust that the error()
method performs as documented.

And I can't, because it doesn't.

Given this situation, I no longer how to recommend reliable I/O in Perl
by any mechanism less painful than in C.

This is not right.

How did I prove this? By setting up these two tests:

Case 1: Call readline on a handle opened to non-blocking tty on which no
input is available. The read will fail and errno will be set to
EGAIN, but the handle's errno() method lies and returns false
despite having experienced errors on that handle.

Case 2: Call syswrite on a handle opened for append on a filesystem that is
full. The write will fail and errno will be set to ENOSPC, but
the handle's errno() method lies and returns false despite having
experienced errors on that handle.

All that remained true in v5.14, v5.16, and blead. I was expecting errors
to persist on the handle. That is what it is documented to do. And it
doesn't. Not only can we not trust close(), we cannot trust error()
either. Brilliant. I now see why you have come to think it is ok for
errors not to be detected: you're just used to it.

There be some esoteric law whose letter all this nonsense follows, but it
is completely broken with regard to the spirit of "easy things should be easy".
And yes, knowing whether you had an I/O error should be easy. People should
not have to bend over backwards with tricky programming to handle edge cases.

--tom

Tom Christiansen

unread,
Feb 16, 2013, 3:51:44 PM2/16/13
to Ricardo Signes, Lukas Mai, Tom Christiansen, Perl5 Porters Mailing List
>> ...I can both:
>>
>> (1) read from a handle, get undef back with errno set, and yet that
>> handle's error() method returns false.
>>
>> (1) write to a handle, get undef back with errno set, and yet that
>> handle's error() method returns false.

>> [...]

>> How did I prove this? By setting up these two tests:
>>
>> Case 1: Call readline on a handle opened to non-blocking tty on which no
>> input is available. The read will fail and errno will be set to
>> EGAIN, but the handle's errno() method lies and returns false
>> despite having experienced errors on that handle.
>>
>> Case 2: Call syswrite on a handle opened for append on a filesystem that
>> is full. The write will fail and errno will be set to ENOSPC, but
>> the handle's errno() method lies and returns false despite having
>> experienced errors on that handle.

> Could you submit this, especially with a simple program (to be run on a full
> filesystem of the tester's own devising), as a perlbug so we can keep track of
> the issue and try to get it sorted in 5.19?

Certainly. I'll do that. I used ttys and fifos because it was easiest to
get some errnos out of them. Oddly, they do different things if there is
no data available. The fifo gives eof; it does not set errno. The tty does.

I need to get it into a smaller pair of test cases. The existing test is
testing too much. Maybe. My starting point is below my sig, which tests
everything. Its sample output is included in the DATA segment.

I'll whittle it down and submit two separate bugs, one apiece.

Some things are difficult to test. For example, I would also test
it with this;

$ sleep 3; perl this_test
type a quick line<RETURN>

I did that so that I could prove that the nonblocking tty read had no
trouble when there really was typeahead waiting for it. I have no (clear)
idea how to set that up with a real test that doesn't involve manual
intervention.

I used a USB FOB for my full filesystem test. Easiest thing to fill up.

--tom

#!/usr/bin/env perl

use v5.10;
use strict;
use warnings;
use warnings; # FATAL => qw(deprecated closure syntax);
use sigtrap;

use Carp;
use Scalar::Util qw(reftype);
use IO::Handle;
use Fcntl;

use File::Temp qw(tempfile);

use constant {
FILE_ON_FULL_FILESYSTEM => "/Volumes/TCHRISTFOB1/full/bar",
EXISTING_FIFO => "/tmp/fifo",
};

main();
exit();

sub main {
local $SIG{__WARN__} = sub { confess "fatalized warning @_" };
STDOUT->autoflush(1);

test_tty();
test_fifo();
test_file();

say "\nThat's all, folks!";
}

##########################################################################

{
my @_Atexit_Stack; END {
while (my $callback = pop @_Atexit_Stack) {
$callback->();
}
}

sub atexit(&) {
my($callback) = @_;
confess "expected CODE arg" unless reftype($callback) eq "CODE";
push @_Atexit_Stack, $callback;
}

}

sub test_tty {
say "\nTesting TTY\n";
test_read("/dev/tty");
test_write("/dev/tty");
}

sub test_fifo {
say "\nTesting FIFO\n";
my $fifo_name = temp_fifo();
test_read($fifo_name);
test_write($fifo_name);
}

sub test_file {
say "\nTesting FILE\n";
my $file = FILE_ON_FULL_FILESYSTEM;
test_read($file);
test_write($file);
$file = "/tmp/fifo";
test_read(EXISTING_FIFO);
test_write(EXISTING_FIFO, 1);
}

sub temp_fifo {
my $template = "tmpfifo.XXXXXX";
#my $tmpdir = File::Spec->tmpdir();
my(undef, $tmpname) = tempfile($template, OPEN => 0);

if (-e $tmpname) {
if (-p _) {
say "skipping create of pre-existing fifo $tmpname";
}
else {
confess "$tmpname already exists but is not a fifo";
}
}
else {
if (system("mkfifo", $tmpname) == 0) {
say "created fifo $tmpname";
atexit { unlink $tmpname };
}
else {
confess "cannot create $tmpname";
}
}

return $tmpname;
}

sub test_write {
my($path_name, $is_big) = @_;

my $path_fh = undef;

sysopen($path_fh, $path_name, O_RDWR | O_APPEND | O_NONBLOCK)
|| confess "can't sysopen $path_name: $!";

say "opened $path_name for writing";
binmode($path_fh);
$path_fh->autoflush(1);

my $string = "[this is the line of test data]\n";
if ($is_big) {
$string x= 1000;
}

my $slen = length($string);

my $written = syswrite($path_fh, $string, $slen);
if (defined $written) {
say "syswrote $written bytes\n";
if ($written != $slen) {
say "But I wanted to write $slen bytes.";
}
} else {
my $errno = $! || confess "expected errno";
my $has_error = $path_fh->error;
printf "syswrite failed, errno was %d: %s\n", ($errno) x 2;
if (my $has_error = $path_fh->error) {
say "path_fh error was ", fmt_bool($has_error);
} else {
say "ERROR: path_fh is not in an error state after failed syswrite.";
}
}

my $ok = print $path_fh $string;
if ($ok) {
say "Print of $slen successful.";
}
else {
my $errno = $! || confess "expected errno";
my $has_error = $path_fh->error;
printf "print failed, errno was %d: %s\n", ($errno) x 2;

if ($has_error) {
say "path_fh error was ", fmt_bool($has_error);
} else {
say "ERROR: path_fh is not in an error state after failed print.";
}


}
if (close($path_fh)) {
say "closed $path_name";
} else {
say "ERROR: can't close $path_name: $!";
}

}

sub test_read {
my($path_name) = @_;

my $path_fh = undef;

sysopen($path_fh, $path_name, O_RDONLY | O_NONBLOCK)
|| confess "can't sysopen $path_name: $!";

say "opened $path_name for reading";
binmode($path_fh);

say "Blocking status = ", fmt_bool($path_fh->blocking);

print "Enter something, quickly: ";
my $line = readline($path_fh);

if (!defined $line) {
my $errno = defined($line) ? 0 : $!;
my $has_error = $path_fh->error;

say "\nToo late!";

if ($errno || $has_error) {
printf "errno was %d: %s\n", ($errno) x 2;
say "path_fh error was ", fmt_bool($has_error);

if ($errno && !$has_error) {
say "WEIRD: errno set but has_error clear";
}
if ($has_error && !$errno) {
say "WEIRD: has_error set but errno clear";
}

}
else {
say "no errno or error state, must mean got EOF on read";
}

}
else {
say "\nThank you, line length was: ", length($line);
}

if (close($path_fh)) {
say "closed $path_name";
} else {
say "ERROR: can't close $path_name: $!";
}


}


sub fmt_bool {
my($bool) = @_;
return $bool ? "TRUE" : "FALSE";
}

sub fmt_undef {
my($scalar) = @_;
return defined($scalar) ? $scalar : "UNDEF";
}

__END__

Testing TTY

opened /dev/tty for reading
Blocking status = FALSE
Enter something, quickly:
Too late!
errno was 35: Resource temporarily unavailable
path_fh error was FALSE
WEIRD: errno set but has_error clear
closed /dev/tty
opened /dev/tty for writing
syswrote 32 bytes

Print of 32 successful.
closed /dev/tty

Testing FIFO

created fifo tmpfifo.gtQ2yy
opened tmpfifo.gtQ2yy for reading
Blocking status = FALSE
Enter something, quickly:
Too late!
no errno or error state, must mean got EOF on read
closed tmpfifo.gtQ2yy
opened tmpfifo.gtQ2yy for writing
syswrote 32 bytes

Print of 32 successful.
closed tmpfifo.gtQ2yy

Testing FILE

opened /Volumes/TCHRISTFOB1/full/bar for reading
Blocking status = FALSE
Enter something, quickly:
Too late!
no errno or error state, must mean got EOF on read
closed /Volumes/TCHRISTFOB1/full/bar
opened /Volumes/TCHRISTFOB1/full/bar for writing
syswrite failed, errno was 28: No space left on device
ERROR: path_fh is not in an error state after failed syswrite.
print failed, errno was 28: No space left on device
path_fh error was TRUE
ERROR: can't close /Volumes/TCHRISTFOB1/full/bar: No space left on device
opened /tmp/fifo for reading
Blocking status = FALSE
Enter something, quickly:
Too late!
no errno or error state, must mean got EOF on read
closed /tmp/fifo
opened /tmp/fifo for writing
syswrote 8192 bytes

But I wanted to write 32000 bytes.
print failed, errno was 35: Resource temporarily unavailable
path_fh error was TRUE
ERROR: can't close /tmp/fifo: Resource temporarily unavailable

That's all, folks!

Tom Christiansen

unread,
Feb 16, 2013, 4:26:14 PM2/16/13
to Lukas Mai, Tom Christiansen, Perl5 Porters Mailing List
> '||' in void context is more of a personal style issue. I generally use
> 'or' in void context and '||' elsewhere to show whether I'm more
> interested in the boolean result or the control flow side effects. This
> also works well with the very low precedence of 'or'.
> As for 'open' in particular, I've seen too many instances of the 'open
> my $fh, "<", $file || die' bug to even show an example of 'open' and
> '||' in the same statement, especially in a tutorial.

I have never ever used open "or" instead of open ||, nor have I ever once
made a precedence error because of it. (I have however sometimes use open
//, of course, for fork opens. Try that with "or".)

Many people can't always keep track of 19 levels of precedence, and so
prefer parentheses to complexity or guesswork. It's the safest practice.

A tutorial should not make people guess. Just use parens, it best.

--tom

Christian Walde

unread,
Feb 16, 2013, 5:20:25 PM2/16/13
to Ricardo Signes, Tom Christiansen, Alexander Hartmaier, Tom Christiansen, Perl5 Porters Mailing List
On Sat, 16 Feb 2013 16:47:19 +0100, Tom Christiansen <tch...@perl.com>
wrote:

> The very first thing I would do is mine Programming Perl
> for examples of open() and sysopen().
>
> Yes, some are from more advanced topics that are beyond the scope
> of a tutorial on open, but all are clearly examples that people
> will be seeing a lot of.

Is this meant as in "Mine for things worthy of being pointed out
additionally"? If not, could you please clarify what you'd like Alexander
to do with those?

Also, on the topic of looking for examples of open, i would like to
suggest to mine CPAN; by grepping for all uses of open, running them
through a pattern compressor and removing/counting duplicates in order to
get an idea of published types of uses and their frequency.

--
With regards,
Christian Walde

Andreas Koenig

unread,
Feb 17, 2013, 5:39:50 AM2/17/13
to Tom Christiansen, Ricardo Signes, Lukas Mai, Tom Christiansen, Perl5 Porters Mailing List
Tom Christiansen <tch...@perl.com> writes:

> I used a USB FOB for my full filesystem test. Easiest thing to fill
> up.

# date > /dev/full
date: write error: No space left on device


--
andreas

H.Merijn Brand

unread,
Feb 17, 2013, 5:49:36 AM2/17/13
to perl5-...@perl.org
That'd work on Linux and HP-UX (as of 11.23), but not on AIX, at least
not up to and including AIX-5.3.0

# HP-UX .. 11.11
$ ls -l /dev/full
ls: cannot access /dev/full: No such file or directory

# AIX .. 5.3.0
$ ls -l /dev/full
ls: /dev/full: A file or directory in the path name does not exist.

Tom Christiansen

unread,
Feb 17, 2013, 12:25:18 PM2/17/13
to Andreas Koenig, Ricardo Signes, Lukas Mai, Tom Christiansen, Perl5 Porters Mailing List
Andreas Koenig <andreas.koe...@franz.ak.mind.de> wrote
on Sun, 17 Feb 2013 11:39:50 +0100:

>> I used a USB FOB for my full filesystem test. Easiest thing to fill
>> up.

># date > /dev/full
>date: write error: No space left on device

I tried that, but it exists on neither OpenBSD nor Darwin.

It would also be nice to have a /dev/eieio that gives random
I/O errors. :)

Actually, it would be cool to have a

/dev/errno/ENOSPC
/dev/errno/EIO
/dev/errno/EAGAIN
/dev/errno/ETIMEDOUT

etc. You get the idea.

I don't know how to create a pseudo-device that produces a
certain errno for certain operations. I'm not even sure
what sort of interface I want for it. I just know that it
would make testing much easier.

--tom

Dr.Ruud

unread,
Feb 17, 2013, 2:21:15 PM2/17/13
to perl5-...@perl.org
On 2013-02-16 21:09, Tom Christiansen wrote:

> [...] Given this situation, I no longer how to recommend reliable I/O in Perl
> by any mechanism less painful than in C.
>
> This is not right.
>
> How did I prove this? By setting up these two tests:
>
> Case 1: Call readline on a handle opened to non-blocking tty on which no
> input is available. The read will fail and errno will be set to
> EGAIN, but the handle's errno() method lies and returns false
> despite having experienced errors on that handle.

ITYM: "the handle's error() method".

(and don't use $! before calling error() :)


>
> Case 2: Call syswrite on a handle opened for append on a filesystem that is
> full. The write will fail and errno will be set to ENOSPC, but
> the handle's errno() method lies and returns false despite having
> experienced errors on that handle.

Idem.


> [..] Not only can we not trust close(), we cannot trust error()
> either. Brilliant. I now see why you have come to think it is ok for
> errors not to be detected: you're just used to it.

Tom, thanks for showing, and it makes me feel sad too.

--
Ruud

Tom Christiansen

unread,
Feb 17, 2013, 3:27:21 PM2/17/13
to Dr.Ruud, perl5-...@perl.org
"Dr.Ruud" <rvtol+...@isolution.nl> wrote on Sun, 17 Feb 2013 20:21:15 +0100:

>> [..] Not only can we not trust close(), we cannot trust error()
>> either. Brilliant. I now see why you have come to think it is ok for
>> errors not to be detected: you're just used to it.

> Tom, thanks for showing, and it makes me feel sad too.

Here's the minimal test proving that IO::Handle->error() is unreliable for
read errors. Sample expected output included in DATA segment, which says:

opened /dev/tty read-only, non-blocking
handle blocking status = FALSE
Enter something, quickly:
Too late! readline failed
IMPOSSIBLE CASE #1: errno set but has_error clear
errno was EAGAIN (35): Resource temporarily unavailable
path_fh->error was FALSE

Therefore you cannot use fh->error for what it claims to be useful for.

Therefore you cannot use fh->error.

Tested with v5.8.8,
v5.10.0,
v5.12.3,
v5.14.1,
v5.16.0,
blead.

--tom

#!/usr/bin/env perl

#########################################################################
# IMPORTANT: *ALWAYS* use v5.6 for handle autoviv, or all is in vain
#########################################################################
use v5.6; # needed for handle autoviv

use strict; # first released in v5, already covered by use v5.6

use warnings; # first released in v5.6, already covered by use v5.6
use warnings FATAL => qw(deprecated closure syntax);

use sigtrap; # first released in v5, already covered by use v5.6

use Carp; # first released in v5, already covered by use v5.6
use Fcntl; # first released in v5, already covered by use v5.6
use Errno; # first released in v5.5.4, already covered by use v5.6

# IO::Handle first released in v5.3.7, but not autoloaded till v5.14!!
use if ($] < 5.014), "IO::Handle";

main();
exit();

##########################################################################

sub main {
local $SIG{__WARN__} = sub { confess "fatalized warning @_" };
STDOUT->autoflush(1);
test_tty();
print "\nThat's all, folks!\n";
}

##########################################################################

sub test_tty {
print "\nTesting TTY\n\n";
test_nonblocking_read("/dev/tty");
}

sub test_nonblocking_read {
my($path_name) = @_;

my $path_fh = undef;
sysopen($path_fh, $path_name, O_RDONLY | O_NONBLOCK)
|| confess "can't sysopen $path_name: $!";
print "opened $path_name read-only, non-blocking\n";
binmode($path_fh) || confess "binmode failed";
print "handle blocking status = ", fmt_bool($path_fh->blocking), "\n";
confess "need nonblocking read" if $path_fh->blocking;

print "Enter something, quickly: ";
my $line = readline($path_fh);
if (defined $line) {
print "\nThank you, line length was: ", length($line), "\n";
}
else {
################################################################
# CRITICAL: must seize errno IMMEDIATELY lest it be lost
################################################################
my $errno = $! || 0;
my $has_error = $path_fh->error;
print "\nToo late! readline failed\n";

if ($errno || $has_error) {
if ($errno && !$has_error) {
print "IMPOSSIBLE CASE #1: errno set but has_error clear\n";
}
if ($has_error && !$errno) {
print "IMPOSSIBLE CASE #2: has_error set but errno clear\n";
}
print "errno was ", fmt_errno($errno), "\n";
print "path_fh->error was ", fmt_bool($has_error), "\n";
}
else {
print "no errno or error state, must mean got EOF on read\n";
}

}
print "\n";

if (close($path_fh)) {
print "closed $path_name\n";
} else {
print "ERROR: can't close $path_name: $!\n";
}


}

sub fmt_bool {
my($bool) = @_;
return $bool ? "TRUE" : "FALSE";
}

sub fmt_undef {
my($scalar) = @_;
return defined($scalar) ? $scalar : "UNDEF";
}

BEGIN {
my @_Errno2Sym;

for my $errname ( grep /^ E \p{Lu}+ $/x, @Errno::EXPORT_OK ) {
my $errnum = do {
no strict "refs";
&{"Errno::$errname"};
};
$_Errno2Sym[$errnum] = $errname;
}

sub fmt_errno {
my $num = 0 + $_[0];
my $strerror = do {
local $! = $num;
"$!";
};
my $esym = $_Errno2Sym[$num] || "unknown error number #$num";
return "$esym ($num): $strerror";
}

}

__END__

Testing TTY

opened /dev/tty read-only, non-blocking
handle blocking status = FALSE
Enter something, quickly:
Too late! readline failed
IMPOSSIBLE CASE #1: errno set but has_error clear
errno was EAGAIN (35): Resource temporarily unavailable
path_fh->error was FALSE

closed /dev/tty

That's all, folks!

zengargoyle

unread,
Feb 17, 2013, 6:51:51 PM2/17/13
to Tom Christiansen, Perl5 Porters Mailing List
On Sun, Feb 17, 2013 at 9:25 AM, Tom Christiansen <tch...@perl.com> wrote:
> Actually, it would be cool to have a
>
> /dev/errno/ENOSPC
> /dev/errno/EIO
> /dev/errno/EAGAIN
> /dev/errno/ETIMEDOUT
>
> etc. You get the idea.

This intrigued me and made me think of FUSE http://fuse.sourceforge.net/ which
(at least for Linux) could do this. Here's a link to the FUSE wiki
showing the example
`hello` filesystem:
http://sourceforge.net/apps/mediawiki/fuse/index.php?title=Hello_World

Someone with a little more C and filesystem chops than myself could
easily make an
'errno' filesystem return errors on demand. There is even a Perl module
https://metacpan.org/release/Fuse if testing Perl file access using a
Perl module that
implements a filesystem doesn't sound like crazy talk.

----
zengargoyle

Paul Johnson

unread,
Mar 1, 2013, 8:49:49 AM3/1/13
to Tom Christiansen, Andreas Koenig, Ricardo Signes, Lukas Mai, Tom Christiansen, Perl5 Porters Mailing List
You might be interested in libfiu - Fault injection in userspace.
See http://blitiri.com.ar/p/libfiu/

I've not used it, and I have no idea whether it works on either OpenBSD
or Darwin, but I like the idea.

I wonder how useful it would be to run against the perl test suite.
Probably not very. We would need specific tests for error conditions, I
expect. But running against those tests would be useful.

--
Paul Johnson - pa...@pjcj.net
http://www.pjcj.net

zengargoyle

unread,
Mar 1, 2013, 11:10:00 AM3/1/13
to Perl5 Porters Mailing List
I was pondering the desired interface for something like this and thought
it might work something like this...

/mnt/error/open/$ERROR ($ERROR is EACCES, EEXIST, ...)
returns the requested error on open

/mnt/error/read/$ERROR ($ERROR is EAGAIN, EBADF, ...)
returns fh that returns desired error on read

/mnt/error/write/$ERROR
returns fh that returns desired error on write

And say a read/seek/close on /mnt/error/write/$ERROR returns success

$ cat /mnt/error/open/ENOENT
cat: /mnt/error/open/ENOENT: No such file or directory

$ cat /mnt/error/open/EACCES
cat: /mnt/error/open/EACCES: Permission denied

$ echo > /mnt/error/write/EPIPE
echo: Pipe closed unexpectedly (or whatever)

The fuse example seemed simple enough and is maybe workable on multiple
platforms (fuse has ports of sorts for the *NIXy systems and Windows).
0 new messages