But it's not, or at least not completely invalid. ...
Good point. Actually, when taking a closer look, I think Perl sets
EBADF one routine further down in PerlIOBase_read() (which is
being called from the macro Perl_PerlIO_or_Base), in case
the PERLIO_F_CANREAD flag isn't set:
PerlIOBase_read(pTHX_ PerlIO *f, void *vbuf, Size_t count)
{
STDCHAR *buf = (STDCHAR *) vbuf;
if (f) {
if (!(PerlIOBase(f)->flags & PERLIO_F_CANREAD)) {
PerlIOBase(f)->flags |= PERLIO_F_ERROR;
SETERRNO(EBADF, SS_IVCHAN);
return 0;
}
...
It's more like Perl remembers the handle's original mode and
doesn't realize it can read from it now.
Yes, and that's most likely because the dup2 doesn't copy
the perl-internal PERLIO* flags (well, how should it, it knows nothing about them).
The following snippet shows that the two STDOUTs modes differ
depending on whether STDOUT is explicitly being closed first:
(I made use of Inline::C because I couldn't find a way to call PerlIO_modestr() directly via plain Perl)
#!/usr/bin/perl
use Inline C;
close STDOUT if $ARGV[0];
open(STDOUT, '+>', "/tmp/stdout.log") or die $!;
dumpmode(STDOUT);
__END__
__C__
void dumpmode(SV* fh) {
char buf[10];
PerlIO *f = IoIFP(sv_2io(fh));
PerlIO_modestr(f, buf);
fprintf(stderr, "mode = %s\n", buf);
}
Output:
$ ./802590.pl 0
mode = w
$ ./802590.pl 1 # with explicit close
mode = r+
Not really sure why it says "r+" instead of "w+", but I
suspect it's because the "+>" internally maps to the same mode as "+<",
after having clobbered the file...
Also, if you set PERLIO_DEBUG, you can see that the "w+"
mode is being applied to the PerlIO layers of fd 1 only in case it is
properly closed/opened:
$ PERLIO_DEBUG=/dev/tty ./802517.pl 1 # with explicit close
...
./802517.pl:0 openn(perlio,'(Null)','Iw',1,0,0,(nil),0,(nil))
./802517.pl:0 Layer 0 is unix
./802517.pl:0 Layer 0 is unix
./802517.pl:0 PerlIO_push f=0x6253c0 unix w 0x603b08
./802517.pl:0 fd 1 refcnt=1
./802517.pl:0 PerlIO_push f=0x6253c0 perlio Iw 0x603b08
./802517.pl:0 Layer 1 is perlio
...
./802517.pl:15 openn(perlio,'','w+',-1,0,0,(nil),1,0x60a178)
./802517.pl:15 Layer 0 is unix
./802517.pl:15 Layer 0 is unix
./802517.pl:15 PerlIO_push f=0x6253c0 unix w+ 0x603b08
./802517.pl:15 fd 1 refcnt=1
./802517.pl:15 PerlIO_push f=0x6253c0 perlio w+ 0x603b08
Otherwise, the "w+" is being applied to a different fd (here fd 8),
and thus disappears together with the fd when it is closed (after the dup2):
$ PERLIO_DEBUG=/dev/tty ./802517.pl 0
...
./802517.pl:0 openn(perlio,'(Null)','Iw',1,0,0,(nil),0,(nil))
./802517.pl:0 Layer 0 is unix
./802517.pl:0 Layer 0 is unix
./802517.pl:0 PerlIO_push f=0x6253c0 unix w 0x603b08
./802517.pl:0 fd 1 refcnt=1
./802517.pl:0 PerlIO_push f=0x6253c0 perlio Iw 0x603b08
./802517.pl:0 Layer 1 is perlio
...
./802517.pl:15 openn(perlio,'','w+',-1,0,0,(nil),1,0x60a178)
./802517.pl:15 Layer 0 is unix
./802517.pl:15 Layer 0 is unix
./802517.pl:15 PerlIO_push f=0x6253e8 unix w+ 0x603b08
./802517.pl:15 fd 8 refcnt=1
./802517.pl:15 PerlIO_push f=0x6253e8 perlio w+ 0x603b08
./802517.pl:15 fd 8 refcnt=0
./802517.pl:15 PerlIO_pop f=0x6253e8 perlio
./802517.pl:15 PerlIO_pop f=0x6253e8 unix
(irrelevant parts snippet) |