#!./perl
BEGIN {
chdir
't'
if
-d
't'
;
require
'./test.pl'
;
set_up_inc(
'../lib'
);
skip_all(
'Can\'t run under miniperl'
)
if
is_miniperl();
}
use
Fcntl
qw(SEEK_SET SEEK_CUR SEEK_END)
;
plan(128);
my
$fh
;
my
$var
=
"aaa\n"
;
ok(
open
(
$fh
,
"+<"
,\
$var
));
is(<
$fh
>,
$var
);
ok(
eof
(
$fh
));
ok(
seek
(
$fh
,0,SEEK_SET));
ok(!
eof
(
$fh
));
ok(
print
$fh
"bbb\n"
);
is(
$var
,
"bbb\n"
);
$var
=
"foo\nbar\n"
;
ok(
seek
(
$fh
,0,SEEK_SET));
ok(!
eof
(
$fh
));
is(<
$fh
>,
"foo\n"
);
ok(
close
$fh
, $!);
$var
=
"Something"
;
open
$fh
,
">"
, \
$var
;
is(
$var
,
""
);
my
$off
=
tell
(
$fh
);
is(
$off
, 0);
$var
=
"Something"
;
print
$fh
"Brea"
;
$off
=
tell
(
$fh
);
is(
$off
, 4);
is(
$var
,
"Breathing"
);
close
$fh
;
$var
=
"Something "
;
open
$fh
,
">>"
, \
$var
;
$off
=
tell
(
$fh
);
is(
$off
, 10);
is(
$var
,
"Something "
);
$var
.=
"else "
;
is(
$var
,
"Something else "
);
$off
=
tell
(
$fh
);
is(
$off
, 10);
print
$fh
"is here"
;
is(
$var
,
"Something else is here"
);
close
$fh
;
$var
=
"line one\nline two\line three\n"
;
open
$fh
,
"<"
, \
$var
;
while
(<
$fh
>) {
$var
=
"foo"
;
}
close
$fh
;
is(
$var
,
"foo"
);
$var
=
''
;
open
$fh
,
"+>"
, \
$var
;
print
$fh
"xxx\n"
;
open
my
$dup
,
'+<&'
,
$fh
;
print
$dup
"yyy\n"
;
seek
(
$dup
,0,SEEK_SET);
is(<
$dup
>,
"xxx\n"
);
is(<
$dup
>,
"yyy\n"
);
close
(
$fh
);
close
(
$dup
);
open
$fh
,
'<'
, \42;
is(<
$fh
>,
"42"
,
"reading from non-string scalars"
);
close
$fh
;
{
package
P;
sub
TIESCALAR {
bless
{}}
sub
FETCH {
"shazam"
}
sub
STORE {} }
tie
my
$p
,
'P'
;
open
$fh
,
'<'
, \
$p
;
is(<
$fh
>,
"shazam"
,
"reading from magic scalars"
);
{
my
$warn
= 0;
local
$SIG
{__WARN__} =
sub
{
$warn
++ };
open
my
$fh
,
'>'
, \
my
$scalar
;
print
$fh
"foo"
;
close
$fh
;
is(
$warn
, 0,
"no warnings when writing to an undefined scalar"
);
undef
$scalar
;
open
$fh
,
'>>'
, \
$scalar
;
print
$fh
"oof"
;
close
$fh
;
is(
$warn
, 0,
"no warnings when appending to an undefined scalar"
);
}
{
my
$warn
= 0;
local
$SIG
{__WARN__} =
sub
{
$warn
++ };
for
(1..2) {
open
my
$fh
,
'>'
, \
my
$scalar
;
close
$fh
;
}
is(
$warn
, 0,
"no warnings when reusing a lexical"
);
}
{
my
$warn
= 0;
local
$SIG
{__WARN__} =
sub
{
$warn
++ };
my
$fetch
= 0;
{
sub
TIESCALAR {
bless
[] }
sub
FETCH {
$fetch
++;
return
undef
}
sub
STORE {}
}
tie
my
$scalar
,
'MgUndef'
;
open
my
$fh
,
'<'
, \
$scalar
;
close
$fh
;
is(
$warn
, 0,
"no warnings reading a magical undef scalar"
);
is(
$fetch
, 1,
"FETCH only called once"
);
}
{
my
$warn
= 0;
local
$SIG
{__WARN__} =
sub
{
$warn
++ };
my
$scalar
= 3;
undef
$scalar
;
open
my
$fh
,
'<'
, \
$scalar
;
close
$fh
;
is(
$warn
, 0,
"no warnings reading an undef, allocated scalar"
);
}
my
$data
=
"a non-empty PV"
;
$data
=
undef
;
open
(MEM,
'<'
, \
$data
) or
die
"Fail: $!\n"
;
my
$x
=
join
''
, <MEM>;
is(
$x
,
''
);
{
my
$s
=
<<'EOF';
line A
line B
a third line
EOF
open
(F,
'<'
, \
$s
) or
die
"Could not open string as a file"
;
local
$/ =
""
;
my
$ln
= <F>;
close
F;
is(
$ln
,
$s
,
"[perl #35929]"
);
}
{
my
$warn
;
local
$SIG
{__WARN__} =
sub
{
$warn
=
"@_"
};
ok(!(
defined
open
(F,
'>'
, \
undef
)),
"[perl #40267] - $!"
);
is(
$warn
,
undef
,
"no warning with warnings off"
);
close
F;
undef
$warn
;
my
$ro
= \43;
ok(!(
defined
open
(F,
'>'
,
$ro
)), $!);
is($!+0, EACCES,
"check we get a read-onlyish error code"
);
like(
$warn
,
qr/Modification of a read-only value attempted/
,
"check we did warn"
);
close
F;
ok(
open
(F,
'<'
,
$ro
), $!);
is(<F>, 43);
close
F;
}
{
my
$foo
;
ok(
open
(F,
'>'
, \
$foo
));
ok(
seek
(F, 50, SEEK_SET));
print
F
"x"
;
is(
length
(
$foo
), 51);
like(
$foo
,
qr/^\0{50}x$/
);
is(
tell
(F), 51);
ok(
seek
(F, 0, SEEK_SET));
is(
length
(
$foo
), 51);
ok(
seek
(F, 100, SEEK_SET));
print
F
"y"
;
is(
length
(
$foo
), 101);
like(
$foo
,
qr/^\0{50}x\0{49}y$/
);
is(
tell
(F), 101);
ok(
seek
(F, 75, SEEK_SET));
print
F
"z"
;
is(
length
(
$foo
), 101);
like(
$foo
,
qr/^\0{50}x\0{24}z\0{24}y$/
);
is(
tell
(F), 76);
ok(!
seek
(F, -50, SEEK_SET), $!);
ok(
seek
(F, 0, SEEK_SET));
ok(!
seek
(F, -50, SEEK_CUR), $!);
ok(!
seek
(F, -150, SEEK_END), $!);
}
{
my
$s
;
sub
TIESCALAR {
bless
\
my
$x
}
sub
FETCH {
$s
.=
':F'
; ${
$_
[0]} }
sub
STORE {
$s
.=
":S($_[1])"
; ${
$_
[0]} =
$_
[1] }
my
$x
;
$s
=
''
;
tie
$x
,
'TS'
;
my
$fh
;
ok(
open
(
$fh
,
'>'
, \
$x
),
'open-write tied scalar'
);
$s
.=
':O'
;
print
(
$fh
'ABC'
);
$s
.=
':P'
;
ok(
seek
(
$fh
, 0, SEEK_SET));
$s
.=
':SK'
;
print
(
$fh
'DEF'
);
$s
.=
':P'
;
ok(
close
(
$fh
),
'close tied scalar - write'
);
is(
$s
,
':F:S():O:F:S(ABC):P:SK:F:S(DEF):P'
,
'tied actions - write'
);
is(
$x
,
'DEF'
,
'new value preserved'
);
$x
=
'GHI'
;
$s
=
''
;
ok(
open
(
$fh
,
'+<'
, \
$x
),
'open-read tied scalar'
);
$s
.=
':O'
;
my
$buf
;
is(
read
(
$fh
,
$buf
,2), 2,
'read1'
);
$s
.=
':R'
;
is(
$buf
,
'GH'
,
'buf1'
);
is(
read
(
$fh
,
$buf
,2), 1,
'read2'
);
$s
.=
':R'
;
is(
$buf
,
'I'
,
'buf2'
);
is(
read
(
$fh
,
$buf
,2), 0,
'read3'
);
$s
.=
':R'
;
is(
$buf
,
''
,
'buf3'
);
ok(
close
(
$fh
),
'close tied scalar - read'
);
is(
$s
,
':F:S(GHI):O:F:R:F:R:F:R'
,
'tied actions - read'
);
}
{
my
$str
=
'1234567890'
;
open
my
$strIn
,
'<'
, \
$str
;
seek
$strIn
, 15, 1;
is
read
(
$strIn
,
my
$buffer
, 5), 0,
'seek beyond end end of string followed by read'
;
}
{
my
$bovid
= __PACKAGE__;
open
my
$handel
,
">"
, \
$bovid
;
print
$handel
"the COW with the crumpled horn"
;
is
$bovid
,
"the COW with the crumpled horn"
,
'writing to COW scalars'
;
package
lrcg {
use
overload
fallback
=> 1,
'""'
=>
sub
{
'chin'
} }
seek
$handel
, 3, 0;
$bovid
=
bless
[], lrcg::;
print
$handel
'mney'
;
is
$bovid
,
'chimney'
,
'writing to refs'
;
seek
$handel
, 1, 0;
$bovid
= 42;
print
$handel
5;
is
$bovid
, 45,
'writing to numeric scalar'
;
seek
$handel
, 1, 0;
undef
$bovid
;
$bovid
= 42;
print
$handel
5;
is
$bovid
, 45,
'writing to numeric scalar'
;
}
{
open
my
$fh
,
"<"
, \(
my
$f
=
*f
);
seek
$fh
, 2,1;
pass
'seeking on a glob copy'
;
open
my
$fh
,
"<"
, \(
my
$f
=
*f
);
seek
$fh
, -2,2;
pass
'seeking on a glob copy from the end'
;
}
sub
has_trailing_nul(\$) {
my
(
$ref
) =
@_
;
my
$sv
= B::svref_2object(
$ref
);
return
undef
if
!
$sv
->isa(
'B::PV'
);
my
$cur
=
$sv
->CUR;
my
$len
=
$sv
->LEN;
return
0
if
$cur
>=
$len
;
my
$ptrlen
=
length
(
pack
(
'P'
,
''
));
my
$ptrfmt
=
$ptrlen
==
length
(
pack
(
'J'
, 0)) ?
'J'
:
$ptrlen
==
length
(
pack
(
'I'
, 0)) ?
'I'
:
die
"Can't determine pointer format"
;
my
$pv_addr
=
unpack
$ptrfmt
,
pack
'P'
,
$$ref
;
my
$trailing
=
unpack
'P'
,
pack
$ptrfmt
,
$pv_addr
+
$cur
;
return
$trailing
eq
"\0"
;
}
SKIP: {
if
(
$Config::Config
{
'extensions'
} !~ m!\bB\b!) {
skip
"no B"
, 4;
}
open
my
$fh
,
">"
, \
my
$memfile
or
die
$!;
print
$fh
"abc"
;
ok has_trailing_nul
$memfile
,
'write appends trailing null when growing string'
;
seek
$fh
, 0,SEEK_SET;
print
$fh
"abc"
;
ok has_trailing_nul
$memfile
,
'write appends trailing null when not growing string'
;
seek
$fh
, 200, SEEK_SET;
print
$fh
"abc"
;
ok has_trailing_nul
$memfile
,
'write appends null when growing string after seek past end'
;
open
$fh
,
">"
, \(
$memfile
=
"hello"
);
ok has_trailing_nul
$memfile
,
'initial truncation in ">" mode provides trailing null'
;
}
SKIP: {
skip
"no threads"
, 2
if
!
$Config::Config
{useithreads};
my
$str
=
''
;
open
my
$fh
,
">"
, \
$str
;
$str
=
'a'
;
is
scalar
threads::async(
sub
{
my
$foo
=
$str
;
$foo
})->
join
,
"a"
,
'scalars behind in-memory handles are cloned properly'
;
print
$fh
"a"
;
is
scalar
threads::async(
sub
{
print
$fh
"b"
;
$str
})->
join
,
"ab"
,
'printing to a cloned in-memory handle works'
;
}
{
open
FILE,
'>'
, \
my
$content
or
die
"Couldn't open scalar filehandle"
;
open
my
$fh
,
">&=FILE"
or
die
"Couldn't open: $!"
;
print
$fh
"Foo-Bar\n"
;
close
$fh
;
close
FILE;
is
$content
,
"Foo-Bar\n"
,
'duping via >&='
;
}
my
$byte_warning
=
"Strings with code points over 0xFF may not be mapped into in-memory file handles\n"
;
{
my
@warnings
;
local
$SIG
{__WARN__} =
sub
{
push
@warnings
,
"@_"
};
my
$content
=
"12\x{101}"
;
$! = 0;
ok(!
open
(
my
$fh
,
"<"
, \
$content
),
"non-byte open should fail"
);
is(0+$!, EINVAL,
"check \$! is updated"
);
is(
@warnings
, 0,
"should be no warnings (yet)"
);
$! = 0;
ok(!
open
(
my
$fh
,
"<"
, \
$content
),
"non byte open should fail (and warn)"
);
is(0+$!, EINVAL,
"check \$! is updated even when we warn"
);
is(
@warnings
, 1,
"should have warned"
);
is(
$warnings
[0],
$byte_warning
,
"should have warned"
);
@warnings
= ();
$content
=
"12\xA1"
;
utf8::upgrade(
$content
);
ok(
open
(
my
$fh
,
"<"
, \
$content
),
"open upgraded scalar"
);
binmode
$fh
;
my
$tmp
;
is(
read
(
$fh
,
$tmp
, 4), 3,
"read should get the downgraded bytes"
);
is(
$tmp
,
"12\xA1"
,
"check we got the expected bytes"
);
close
$fh
;
is(
@warnings
, 0,
"should be no more warnings"
);
}
{
my
$content
=
"abc"
;
ok(
open
(
my
$fh
,
"+<"
, \
$content
),
"open a scalar"
);
binmode
$fh
;
my
$tmp
;
is(
read
(
$fh
,
$tmp
, 1), 1,
"basic read"
);
seek
(
$fh
, 1, SEEK_SET);
$content
=
"\xA1\xA2\xA3"
;
utf8::upgrade(
$content
);
is(
read
(
$fh
,
$tmp
, 1), 1,
"read from post-open upgraded scalar"
);
is(
$tmp
,
"\xA2"
,
"check we read the correct value"
);
seek
(
$fh
, 1, SEEK_SET);
$content
=
"\x{101}\x{102}\x{103}"
;
my
@warnings
;
local
$SIG
{__WARN__} =
sub
{
push
@warnings
,
"@_"
};
$! = 0;
is(
read
(
$fh
,
$tmp
, 1),
undef
,
"read from scalar with >0xff chars"
);
is(0+$!, EINVAL,
"check errno set correctly"
);
is(
@warnings
, 0,
"should be no warnings (yet)"
);
seek
(
$fh
, 1, SEEK_SET);
is(
read
(
$fh
,
$tmp
, 1),
undef
,
"read from scalar with >0xff chars"
);
is(
@warnings
, 1,
"check warnings"
);
is(
$warnings
[0],
$byte_warning
,
"check warnings"
);
select
$fh
;
$| = 1;
select
STDERR;
no
warnings
"utf8"
;
@warnings
= ();
$content
=
"\xA1\xA2\xA3"
;
utf8::upgrade(
$content
);
seek
(
$fh
, 1, SEEK_SET);
ok((
print
$fh
"A"
),
"print to an upgraded byte string"
);
seek
(
$fh
, 1, SEEK_SET);
is(
$content
,
"\xA1A\xA3"
,
"check result"
);
$content
=
"\x{101}\x{102}\x{103}"
;
$! = 0;
ok(!(
print
$fh
"B"
),
"write to an non-downgradable SV"
);
is(0+$!, EINVAL,
"check errno set"
);
is(
@warnings
, 0,
"should be no warning"
);
ok(!(
print
$fh
"B"
),
"write to an non-downgradable SV (and warn)"
);
is(
@warnings
, 1,
"check warnings"
);
is(
$warnings
[0],
$byte_warning
,
"check warnings"
);
}
{
my
$x
= \42;
open
my
$fh
,
"<"
, \
$x
;
my
$got
= <
$fh
>;
like(
$got
,
qr/^SCALAR\(0x[0-9a-f]+\)$/
,
"ref to a ref"
);
is
ref
$x
,
"SCALAR"
,
"target scalar is still a reference"
;
}
{
my
$x
= \42;
my
$as_string
=
"$x"
;
open
my
$refh
,
">>"
, \
$x
;
is
ref
$x
,
"SCALAR"
,
'still a ref after opening for appending'
;
print
$refh
"boo\n"
;
is
$x
,
$as_string
.
"boo\n"
,
'string gets appended to ref'
;
}
SKIP:
{
skip
"Can't seek over 4GB with a small off_t"
, 4
if
$Config::Config
{lseeksize} < 8;
my
$buf0
=
"hello"
;
open
my
$fh
,
"<"
, \
$buf0
or
die
$!;
ok(
seek
(
$fh
, 2**32, SEEK_SET),
"seek to a large position"
);
is(
read
(
$fh
,
my
$tmp
, 1), 0,
"read from a large offset"
);
is(
$tmp
,
""
,
"should have read nothing"
);
ok(
eof
(
$fh
),
"fh should be eof"
);
}
{
my
$buf0
=
"hello"
;
open
my
$fh
,
"<"
, \
$buf0
or
die
$!;
ok(!
seek
(
$fh
, -10, SEEK_CUR),
"seek to negative position"
);
is(
tell
(
$fh
), 0,
"shouldn't change the position"
);
}
SKIP:
{
skip
"Can't overflow SSize_t with Off_t"
, 2
if
$Config::Config
{lseeksize} <=
$Config::Config
{sizesize};
my
$buf0
=
"hello"
;
open
my
$fh
,
"+<"
, \
$buf0
or
die
$!;
ok(
seek
(
$fh
, 2**32, SEEK_SET),
"seek to a large position"
);
select
((
select
(
$fh
), ++$|)[0]);
ok(!(
print
$fh
"x"
),
"write to a large offset"
);
}