Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

scalar filehandle and truncate/seek #16008

Open
p5pRT opened this issue Jun 9, 2017 · 11 comments
Open

scalar filehandle and truncate/seek #16008

p5pRT opened this issue Jun 9, 2017 · 11 comments

Comments

@p5pRT
Copy link

p5pRT commented Jun 9, 2017

Migrated from rt.perl.org#131544 (status was 'open')

Searchable as RT131544$

@p5pRT
Copy link
Author

p5pRT commented Jun 9, 2017

From cm.perl@abtela.com

While writing tests for a rollback mechanism, I chanced on a limitation
of scalar filehandles. While you apparently can truncate regular files,
opened either for append or simple write, e.g.

  [cm@​cos7 ~/perl]$ rm -f tmp.dat
  [cm@​cos7 ~/perl]$ ../localperl/bin/perl5.25.9 -e 'open my $h, ">>",
"tmp.dat"; print $h "hello earthlings\n"; $h->truncate(6) or warn $!;
$h->seek(6,0); print $h "world\n"; close $h or die $!; print $x'
  [cm@​cos7 ~/perl]$ cat tmp.dat
  hello world
  [cm@​cos7 ~/perl]$ rm -f tmp.dat
  [cm@​cos7 ~/perl]$ ../localperl/bin/perl5.25.9 -e 'open my $h, ">",
"tmp.dat"; print $h "hello earthlings\n"; $h->truncate(6) or warn $!;
$h->seek(6,0); print $h "world\n"; close $h or die $!; print $x'
  [cm@​cos7 ~/perl]$ cat tmp.dat
  hello world
  [cm@​cos7 ~/perl]$

a very similar code fails on a scalar filehandle :

  [cm@​cos7 ~/perl]$ ../localperl/bin/perl5.25.9 -e 'open my $h, ">>",
\ my $x; print $h "hello earthlings\n"; $h->truncate(6) or warn $!;
$h->seek(6,0); print $h "world\n"; close $h or die $!; print $x'
  Bad file descriptor at -e line 1.
  hello earthlings
  world
  [cm@​cos7 ~/perl]$ ../localperl/bin/perl5.25.9 -e 'open my $h, ">", \
my $x; print $h "hello earthlings\n"; $h->truncate(6) or warn $!;
$h->seek(6,0); print $h "world\n"; close $h or die $!; print $x'
  Bad file descriptor at -e line 1.
  hello world
  ings
  [cm@​cos7 ~/perl]$

(in the latter case the impact of the open mode on the behavior of seek
is also rather disturbing).

This is apparently a long-standing limitation (it gets a mention e.g. in
https://www.effectiveperlprogramming.com/2010/11/dont-modify-scalars-connected-to-string-filehandles/).

As limitations go however, it is a weird one. Any chance that it might
get lifted (or justified and documented) ?

Thanks in advance,

--Christian


Flags​:
  category=core
  severity=low


Site configuration information for perl 5.25.9​:

Configured by cm at Fri Jun 9 09​:44​:55 CEST 2017.

Summary of my perl5 (revision 5 version 25 subversion 9) configuration​:
  Commit id​: bd93adf
  Platform​:
  osname=linux
  osvers=3.10.0-327.el7.x86_64
  archname=x86_64-linux
  uname='linux cos7.localdomain 3.10.0-327.el7.x86_64 #1 smp thu nov
19 22​:10​:57 utc 2015 x86_64 x86_64 x86_64 gnulinux '
  config_args='-des -Dprefix=/home/cm/localperl -Dusedevel'
  hint=recommended
  useposix=true
  d_sigaction=define
  useithreads=undef
  usemultiplicity=undef
  use64bitint=define
  use64bitall=define
  uselongdouble=undef
  usemymalloc=n
  bincompat5005=undef
  Compiler​:
  cc='cc'
  ccflags ='-fwrapv -fno-strict-aliasing -pipe
-fstack-protector-strong -I/usr/local/include -D_LARGEFILE_SOURCE
-D_FILE_OFFSET_BITS=64 -D_FORTIFY_SOURCE=2'
  optimize='-O2'
  cppflags='-fwrapv -fno-strict-aliasing -pipe
-fstack-protector-strong -I/usr/local/include'
  ccversion=''
  gccversion='4.8.5 20150623 (Red Hat 4.8.5-4)'
  gccosandvers=''
  intsize=4
  longsize=8
  ptrsize=8
  doublesize=8
  byteorder=12345678
  doublekind=3
  d_longlong=define
  longlongsize=8
  d_longdbl=define
  longdblsize=16
  longdblkind=3
  ivtype='long'
  ivsize=8
  nvtype='double'
  nvsize=8
  Off_t='off_t'
  lseeksize=8
  alignbytes=8
  prototype=define
  Linker and Libraries​:
  ld='cc'
  ldflags =' -fstack-protector-strong -L/usr/local/lib'
  libpth=/usr/local/lib /usr/lib /lib/../lib64 /usr/lib/../lib64 /lib
/lib64 /usr/lib64 /usr/local/lib64
  libs=-lpthread -lnsl -lgdbm -ldb -ldl -lm -lcrypt -lutil -lc
-lgdbm_compat
  perllibs=-lpthread -lnsl -ldl -lm -lcrypt -lutil -lc
  libc=libc-2.17.so
  so=so
  useshrplib=false
  libperl=libperl.a
  gnulibc_version='2.17'
  Dynamic Linking​:
  dlsrc=dl_dlopen.xs
  dlext=so
  d_dlsymun=undef
  ccdlflags='-Wl,-E'
  cccdlflags='-fPIC'
  lddlflags='-shared -O2 -L/usr/local/lib -fstack-protector-strong'


@​INC for perl 5.25.9​:
  /home/cm/localperl/lib/site_perl/5.25.9/x86_64-linux
  /home/cm/localperl/lib/site_perl/5.25.9
  /home/cm/localperl/lib/5.25.9/x86_64-linux
  /home/cm/localperl/lib/5.25.9


Environment for perl 5.25.9​:
  HOME=/home/cm
  LANG=fr_FR.UTF-8
  LANGUAGE (unset)
  LC_ALL=C
  LD_LIBRARY_PATH (unset)
  LOGDIR (unset)
  PATH=/usr/bin​:/usr/sbin​:/usr/local/bin
  PERL_BADLANG (unset)
  SHELL=/bin/bash

@p5pRT
Copy link
Author

p5pRT commented Jun 9, 2017

From @jkeenan

On Fri, 09 Jun 2017 14​:01​:49 GMT, cm.perl@​abtela.com wrote​:

While writing tests for a rollback mechanism, I chanced on a
limitation
of scalar filehandles. While you apparently can truncate regular
files,
opened either for append or simple write, e.g.

[cm@​cos7 ~/perl]$ rm -f tmp.dat
[cm@​cos7 ~/perl]$ ../localperl/bin/perl5.25.9 -e 'open my $h, ">>",
"tmp.dat"; print $h "hello earthlings\n"; $h->truncate(6) or warn $!;
$h->seek(6,0); print $h "world\n"; close $h or die $!; print $x'

Can you clarify what the intent of $x is?

[cm@​cos7 ~/perl]$ cat tmp.dat
hello world
[cm@​cos7 ~/perl]$ rm -f tmp.dat
[cm@​cos7 ~/perl]$ ../localperl/bin/perl5.25.9 -e 'open my $h, ">",
"tmp.dat"; print $h "hello earthlings\n"; $h->truncate(6) or warn $!;
$h->seek(6,0); print $h "world\n"; close $h or die $!; print $x'

Likewise.

[cm@​cos7 ~/perl]$ cat tmp.dat
hello world
[cm@​cos7 ~/perl]$

a very similar code fails on a scalar filehandle :

[cm@​cos7 ~/perl]$ ../localperl/bin/perl5.25.9 -e 'open my $h, ">>",
\ my $x; print $h "hello earthlings\n"; $h->truncate(6) or warn $!;
$h->seek(6,0); print $h "world\n"; close $h or die $!; print $x'

Likewise.

Bad file descriptor at -e line 1.
hello earthlings
world
[cm@​cos7 ~/perl]$ ../localperl/bin/perl5.25.9 -e 'open my $h, ">", \
my $x; print $h "hello earthlings\n"; $h->truncate(6) or warn $!;
$h->seek(6,0); print $h "world\n"; close $h or die $!; print $x'

Likewise.

Bad file descriptor at -e line 1.
hello world
ings
[cm@​cos7 ~/perl]$

(in the latter case the impact of the open mode on the behavior of
seek
is also rather disturbing).

This is apparently a long-standing limitation (it gets a mention e.g.
in
https://www.effectiveperlprogramming.com/2010/11/dont-modify-scalars-
connected-to-string-filehandles/).

As limitations go however, it is a weird one. Any chance that it might
get lifted (or justified and documented) ?

Thanks in advance,

--Christian

Thank you very much.
--
James E Keenan (jkeenan@​cpan.org)

@p5pRT
Copy link
Author

p5pRT commented Jun 9, 2017

The RT System itself - Status changed from 'new' to 'open'

@p5pRT
Copy link
Author

p5pRT commented Jun 9, 2017

From @jkeenan

On Fri, 09 Jun 2017 17​:46​:35 GMT, jkeenan wrote​:

On Fri, 09 Jun 2017 14​:01​:49 GMT, cm.perl@​abtela.com wrote​:

While writing tests for a rollback mechanism, I chanced on a
limitation
of scalar filehandles. While you apparently can truncate regular
files,
opened either for append or simple write, e.g.

[cm@​cos7 ~/perl]$ rm -f tmp.dat
[cm@​cos7 ~/perl]$ ../localperl/bin/perl5.25.9 -e 'open my $h, ">>",
"tmp.dat"; print $h "hello earthlings\n"; $h->truncate(6) or warn $!;
$h->seek(6,0); print $h "world\n"; close $h or die $!; print $x'

Can you clarify what the intent of $x is?

[cm@​cos7 ~/perl]$ cat tmp.dat
hello world
[cm@​cos7 ~/perl]$ rm -f tmp.dat
[cm@​cos7 ~/perl]$ ../localperl/bin/perl5.25.9 -e 'open my $h, ">",
"tmp.dat"; print $h "hello earthlings\n"; $h->truncate(6) or warn $!;
$h->seek(6,0); print $h "world\n"; close $h or die $!; print $x'

Likewise.

(Please ignore the two subsequent "Likewise". I was having trouble reading the code as one-liner.)

--
James E Keenan (jkeenan@​cpan.org)

@p5pRT
Copy link
Author

p5pRT commented Jun 9, 2017

From @jkeenan

On Fri, 09 Jun 2017 14​:01​:49 GMT, cm.perl@​abtela.com wrote​:

While writing tests for a rollback mechanism, I chanced on a
limitation
of scalar filehandles. While you apparently can truncate regular
files,
opened either for append or simple write, e.g.

[cm@​cos7 ~/perl]$ rm -f tmp.dat
[cm@​cos7 ~/perl]$ ../localperl/bin/perl5.25.9 -e 'open my $h, ">>",
"tmp.dat"; print $h "hello earthlings\n"; $h->truncate(6) or warn $!;
$h->seek(6,0); print $h "world\n"; close $h or die $!; print $x'
[cm@​cos7 ~/perl]$ cat tmp.dat
hello world
[cm@​cos7 ~/perl]$ rm -f tmp.dat
[cm@​cos7 ~/perl]$ ../localperl/bin/perl5.25.9 -e 'open my $h, ">",
"tmp.dat"; print $h "hello earthlings\n"; $h->truncate(6) or warn $!;
$h->seek(6,0); print $h "world\n"; close $h or die $!; print $x'
[cm@​cos7 ~/perl]$ cat tmp.dat
hello world
[cm@​cos7 ~/perl]$

a very similar code fails on a scalar filehandle :

[cm@​cos7 ~/perl]$ ../localperl/bin/perl5.25.9 -e 'open my $h, ">>",
\ my $x; print $h "hello earthlings\n"; $h->truncate(6) or warn $!;
$h->seek(6,0); print $h "world\n"; close $h or die $!; print $x'
Bad file descriptor at -e line 1.
hello earthlings
world
[cm@​cos7 ~/perl]$ ../localperl/bin/perl5.25.9 -e 'open my $h, ">", \
my $x; print $h "hello earthlings\n"; $h->truncate(6) or warn $!;
$h->seek(6,0); print $h "world\n"; close $h or die $!; print $x'
Bad file descriptor at -e line 1.
hello world
ings
[cm@​cos7 ~/perl]$

(in the latter case the impact of the open mode on the behavior of
seek
is also rather disturbing).

This is apparently a long-standing limitation (it gets a mention e.g.
in
https://www.effectiveperlprogramming.com/2010/11/dont-modify-scalars-
connected-to-string-filehandles/).

As limitations go however, it is a weird one. Any chance that it might
get lifted (or justified and documented) ?

Thanks in advance,

--Christian

The file attached demonstrates that the problem exists independently of the use of the IO​::Handle notation for the 'truncate' and 'seek' calls.

Thank you very much.

--
James E Keenan (jkeenan@​cpan.org)

@p5pRT
Copy link
Author

p5pRT commented Jun 9, 2017

From @jkeenan

131544-truncate.pl

@p5pRT
Copy link
Author

p5pRT commented Jun 12, 2017

From cm.perl@abtela.com

Le 09/06/2017 à 20​:07, James E Keenan via RT a écrit :

On Fri, 09 Jun 2017 14​:01​:49 GMT, cm.perl@​abtela.com wrote​:

While writing tests for a rollback mechanism, I chanced on a
limitation
of scalar filehandles. While you apparently can truncate regular
files,
opened either for append or simple write, e.g.

[cm@​cos7 ~/perl]$ rm -f tmp.dat
[cm@​cos7 ~/perl]$ ../localperl/bin/perl5.25.9 -e 'open my $h, ">>",
"tmp.dat"; print $h "hello earthlings\n"; $h->truncate(6) or warn $!;
$h->seek(6,0); print $h "world\n"; close $h or die $!; print $x'
[cm@​cos7 ~/perl]$ cat tmp.dat
hello world
[cm@​cos7 ~/perl]$ rm -f tmp.dat
[cm@​cos7 ~/perl]$ ../localperl/bin/perl5.25.9 -e 'open my $h, ">",
"tmp.dat"; print $h "hello earthlings\n"; $h->truncate(6) or warn $!;
$h->seek(6,0); print $h "world\n"; close $h or die $!; print $x'
[cm@​cos7 ~/perl]$ cat tmp.dat
hello world
[cm@​cos7 ~/perl]$

a very similar code fails on a scalar filehandle :

[cm@​cos7 ~/perl]$ ../localperl/bin/perl5.25.9 -e 'open my $h, ">>",
\ my $x; print $h "hello earthlings\n"; $h->truncate(6) or warn $!;
$h->seek(6,0); print $h "world\n"; close $h or die $!; print $x'
Bad file descriptor at -e line 1.
hello earthlings
world
[cm@​cos7 ~/perl]$ ../localperl/bin/perl5.25.9 -e 'open my $h, ">", \
my $x; print $h "hello earthlings\n"; $h->truncate(6) or warn $!;
$h->seek(6,0); print $h "world\n"; close $h or die $!; print $x'
Bad file descriptor at -e line 1.
hello world
ings
[cm@​cos7 ~/perl]$

(in the latter case the impact of the open mode on the behavior of
seek
is also rather disturbing).

This is apparently a long-standing limitation (it gets a mention e.g.
in
https://www.effectiveperlprogramming.com/2010/11/dont-modify-scalars-
connected-to-string-filehandles/).

As limitations go however, it is a weird one. Any chance that it might
get lifted (or justified and documented) ?

Thanks in advance,

--Christian

The file attached demonstrates that the problem exists independently of the use of the IO​::Handle notation for the 'truncate' and 'seek' calls.

Thank you very much.

Indeed, and thank you for looking at this (sorry for the extraneous
print in my initial post).

FWIW, opening in read-write mode does not help either :

[cm@​cos7 ~/perl]$ export PS2=
[cm@​cos7 ~/perl]$ ../localperl/bin/perl5.25.9 -e '
++$|;
for $mode (qw(+< > >>)) {
  print qq{--- mode "$mode"\n};
  for $what (\my $x, "tmp.dat") {
  unless (ref($what)) {
  unlink $what;
  `touch $what` if $mode eq q{+<};
  }
  open FH, $mode, $what;
  print FH "1234567890"
  or warn "print $what​: $!";
  seek FH, 5, 0
  or warn "seek $what​: $!";
  truncate FH, 5
  or warn "truncate $what​: $!";
  print FH "XYZ"
  or warn "print $what​: $!";
  close FH
  or warn "close $what​: $!";
  printf("%-20s%s\n", ref($what) ? $x : `cat $what`, $what);
  }
}'
--- mode "+<"
truncate SCALAR(0xc3f980)​: Bad file descriptor at -e line 15.
12345XYZ90 SCALAR(0xc3f980)
12345XYZ tmp.dat
--- mode ">"
truncate SCALAR(0xc3f980)​: Bad file descriptor at -e line 15.
12345XYZ90 SCALAR(0xc3f980)
12345XYZ tmp.dat
--- mode ">>"
truncate SCALAR(0xc3f980)​: Bad file descriptor at -e line 15.
1234567890XYZ SCALAR(0xc3f980)
12345XYZ tmp.dat
[cm@​cos7 ~/perl]$

@p5pRT
Copy link
Author

p5pRT commented Jun 13, 2017

From cm.perl@abtela.com

Le 12/06/2017 à 14​:36, Christian Millour a écrit :

Le 09/06/2017 à 20​:07, James E Keenan via RT a écrit :

On Fri, 09 Jun 2017 14​:01​:49 GMT, cm.perl@​abtela.com wrote​:

While writing tests for a rollback mechanism, I chanced on a
limitation
of scalar filehandles. While you apparently can truncate regular
files,
opened either for append or simple write, e.g.

[cm@​cos7 ~/perl]$ rm -f tmp.dat
[cm@​cos7 ~/perl]$ ../localperl/bin/perl5.25.9 -e 'open my $h, ">>",
"tmp.dat"; print $h "hello earthlings\n"; $h-&gt;truncate(6) or warn $!;
$h-&gt;seek(6,0); print $h "world\n"; close $h or die $!; print $x'
[cm@​cos7 ~/perl]$ cat tmp.dat
hello world
[cm@​cos7 ~/perl]$ rm -f tmp.dat
[cm@​cos7 ~/perl]$ ../localperl/bin/perl5.25.9 -e 'open my $h, ">",
"tmp.dat"; print $h "hello earthlings\n"; $h-&gt;truncate(6) or warn $!;
$h-&gt;seek(6,0); print $h "world\n"; close $h or die $!; print $x'
[cm@​cos7 ~/perl]$ cat tmp.dat
hello world
[cm@​cos7 ~/perl]$

a very similar code fails on a scalar filehandle :

[cm@​cos7 ~/perl]$ ../localperl/bin/perl5.25.9 -e 'open my $h, ">>",
\ my $x; print $h "hello earthlings\n"; $h-&gt;truncate(6) or warn $!;
$h-&gt;seek(6,0); print $h "world\n"; close $h or die $!; print $x'
Bad file descriptor at -e line 1.
hello earthlings
world
[cm@​cos7 ~/perl]$ ../localperl/bin/perl5.25.9 -e 'open my $h, ">", \
my $x; print $h "hello earthlings\n"; $h-&gt;truncate(6) or warn $!;
$h-&gt;seek(6,0); print $h "world\n"; close $h or die $!; print $x'
Bad file descriptor at -e line 1.
hello world
ings
[cm@​cos7 ~/perl]$

(in the latter case the impact of the open mode on the behavior of
seek
is also rather disturbing).

This is apparently a long-standing limitation (it gets a mention e.g.
in
https://www.effectiveperlprogramming.com/2010/11/dont-modify-scalars-
connected-to-string-filehandles/).

As limitations go however, it is a weird one. Any chance that it might
get lifted (or justified and documented) ?

Thanks in advance,

--Christian

The file attached demonstrates that the problem exists independently
of the use of the IO​::Handle notation for the 'truncate' and 'seek'
calls.

Thank you very much.

Indeed, and thank you for looking at this (sorry for the extraneous
print in my initial post).

FWIW, opening in read-write mode does not help either :

[cm@​cos7 ~/perl]$ export PS2=
[cm@​cos7 ~/perl]$ ../localperl/bin/perl5.25.9 -e '
++$|;
for $mode (qw(+< > >>)) {
print qq{--- mode "$mode"\n};
for $what (\my $x, "tmp.dat") {
unless (ref($what)) {
unlink $what;
`touch $what` if $mode eq q{+<};
}
open FH, $mode, $what;
print FH "1234567890"
or warn "print $what​: $!";
seek FH, 5, 0
or warn "seek $what​: $!";
truncate FH, 5
or warn "truncate $what​: $!";
print FH "XYZ"
or warn "print $what​: $!";
close FH
or warn "close $what​: $!";
printf("%-20s%s\n", ref($what) ? $x : `cat $what`, $what);
}
}'
--- mode "+<"
truncate SCALAR(0xc3f980)​: Bad file descriptor at -e line 15.
12345XYZ90 SCALAR(0xc3f980)
12345XYZ tmp.dat
--- mode ">"
truncate SCALAR(0xc3f980)​: Bad file descriptor at -e line 15.
12345XYZ90 SCALAR(0xc3f980)
12345XYZ tmp.dat
--- mode ">>"
truncate SCALAR(0xc3f980)​: Bad file descriptor at -e line 15.
1234567890XYZ SCALAR(0xc3f980)
12345XYZ tmp.dat
[cm@​cos7 ~/perl]$

an additional twist on this is that if I comment out the call to
truncate above I get similar results for scalar and regular file handles
but the behavior in append mode is quite unexpected :

[cm@​cos7 ~/perl]$ ../localperl/bin/perl5.25.9 -e '
++$|;
for $mode (qw(+< > >>)) {
  print qq{--- mode "$mode"\n};
  for $what (\my $x, "tmp.dat") {
  unless (ref($what)) {
  unlink $what;
  `touch $what` if $mode eq q{+<};
  }
  open FH, $mode, $what;
  print FH "1234567890"
  or warn "print $what​: $!";
  seek FH, 5, 0
  or warn "seek $what​: $!";
  # truncate FH, 5
  # or warn "truncate $what​: $!";
  print FH "XYZ"
  or warn "print $what​: $!";
  close FH
  or warn "close $what​: $!";
  printf("%-20s%s\n", ref($what) ? $x : `cat $what`, $what);
  }
}'
--- mode "+<"
12345XYZ90 SCALAR(0x26358e0)
12345XYZ90 tmp.dat
--- mode ">"
12345XYZ90 SCALAR(0x26358e0)
12345XYZ90 tmp.dat
--- mode ">>"
1234567890XYZ SCALAR(0x26358e0)
1234567890XYZ tmp.dat
[cm@​cos7 ~/perl]$

It looks like the call to seek is simply ignored in append mode (">>"),
while the seek/print combination does work as expected (or at least as I
would expect it to) in clobber (">") and edit ("+<") modes.

This does not appear to be documented. Any clue anyone ?

@p5pRT
Copy link
Author

p5pRT commented Jun 14, 2017

From @tonycoz

On Fri, 09 Jun 2017 07​:01​:49 -0700, cm.perl@​abtela.com wrote​:

While writing tests for a rollback mechanism, I chanced on a
limitation
of scalar filehandles. While you apparently can truncate regular
files,
opened either for append or simple write, e.g.
...
a very similar code fails on a scalar filehandle :

[cm@​cos7 ~/perl]$ ../localperl/bin/perl5.25.9 -e 'open my $h, ">>",
\ my $x; print $h "hello earthlings\n"; $h-&gt;truncate(6) or warn $!;
$h-&gt;seek(6,0); print $h "world\n"; close $h or die $!; print $x'
Bad file descriptor at -e line 1.
hello earthlings
world
[cm@​cos7 ~/perl]$ ../localperl/bin/perl5.25.9 -e 'open my $h, ">", \
my $x; print $h "hello earthlings\n"; $h-&gt;truncate(6) or warn $!;
$h-&gt;seek(6,0); print $h "world\n"; close $h or die $!; print $x'
Bad file descriptor at -e line 1.
hello world
ings
[cm@​cos7 ~/perl]$

There's no perlio entry point for truncating a file, so the perl truncate implementation directly uses the ftruncate() function (and fails in this case)

There's also no stat() entry point, so C<< -s $fh >> also doesn't work.

(in the latter case the impact of the open mode on the behavior of
seek
is also rather disturbing).

The behaviour of append mode looks like the intended behaviour​: output is always appended at end of file.

This mirrors the behaviour of an "a" mode for fopen​:

  a Open for appending (writing at end of file). The file is cre‐
  ated if it does not exist. The stream is positioned at the end
  of the file.

  a+ Open for reading and appending (writing at end of file). The
  file is created if it does not exist. The initial file position
  for reading is at the beginning of the file, but output is
  always appended to the end of the file.

It could be better documented.

This is apparently a long-standing limitation (it gets a mention e.g.
in
https://www.effectiveperlprogramming.com/2010/11/dont-modify-scalars-
connected-to-string-filehandles/).

As limitations go however, it is a weird one. Any chance that it might
get lifted (or justified and documented) ?

It should be possible to implement by adding a truncate entry to PerlIO_funcs (perliol.h)

Tony

@p5pRT
Copy link
Author

p5pRT commented Jun 14, 2017

From cm.perl@abtela.com

Le 14/06/2017 à 03​:19, Tony Cook via RT a écrit :

On Fri, 09 Jun 2017 07​:01​:49 -0700, cm.perl@​abtela.com wrote​:

While writing tests for a rollback mechanism, I chanced on a
limitation
of scalar filehandles. While you apparently can truncate regular
files,
opened either for append or simple write, e.g.
...
a very similar code fails on a scalar filehandle :

[cm@​cos7 ~/perl]$ ../localperl/bin/perl5.25.9 -e 'open my $h, ">>",
\ my $x; print $h "hello earthlings\n"; $h-&gt;truncate(6) or warn $!;
$h-&gt;seek(6,0); print $h "world\n"; close $h or die $!; print $x'
Bad file descriptor at -e line 1.
hello earthlings
world
[cm@​cos7 ~/perl]$ ../localperl/bin/perl5.25.9 -e 'open my $h, ">", \
my $x; print $h "hello earthlings\n"; $h-&gt;truncate(6) or warn $!;
$h-&gt;seek(6,0); print $h "world\n"; close $h or die $!; print $x'
Bad file descriptor at -e line 1.
hello world
ings
[cm@​cos7 ~/perl]$

There's no perlio entry point for truncating a file, so the perl truncate implementation directly uses the ftruncate() function (and fails in this case)

There's also no stat() entry point, so C<< -s $fh >> also doesn't work.

(in the latter case the impact of the open mode on the behavior of
seek
is also rather disturbing).

The behaviour of append mode looks like the intended behaviour​: output is always appended at end of file.

This mirrors the behaviour of an "a" mode for fopen​:

    a      Open  for  appending \(writing at end of file\)\.  The file is cre‐
           ated if it does not exist\.  The stream is positioned at the  end
           of the file\.

    a\+     Open  for  reading  and appending \(writing at end of file\)\.  The
           file is created if it does not exist\.  The initial file position
           for  reading  is  at  the  beginning  of the file\, but output is
           always appended to the end of the file\.

It could be better documented.

Thank you. It appears I had wrong expectations wrt append mode. Indeed,
most n*x man pages for fopen sport a variant of the Posix 1003.1 one :

  Opening a file with append mode (a as the first character in the
  mode argument) shall cause all subsequent writes to the file to be
  forced to the then current end-of-file, regardless of intervening
  calls to fseek().

e.g. Debian 7.7 :

  Opening a file in append mode (a as the first character of mode)
  causes all subsequent write operations to this stream to occur at
  end-of-file, as if preceded the call​:
 
  fseek(stream,0,SEEK_END);

Which means that the only way to change the contents of a file in append
mode is indeed to truncate and write. It also means that the call to
seek in my code above is superflous in append mode, but not in the other
ones :

[cm@​cos7 /perl]$ ../localperl/bin/perl5.25.9 -e '
++$|;
for $mode (qw(+< > +> >> +>>)) {
  print qq{--- mode "$mode"\n};
  for $what (\my $x, "tmp.dat") {
  unless (ref($what)) {
  unlink $what;
  `touch $what` if $mode eq q{+<};
  }
  open FH, $mode, $what
  or warn "open $what with $mode​: $!";
  print FH "1234567890"
  or warn "print $what​: $!";
  # seek FH, 5, 0
  # or warn "seek $what​: $!";
  truncate FH, 5
  or warn "truncate $what​: $!";
  print FH "XYZ"
  or warn "print $what​: $!";
  close FH
  or warn "close $what​: $!";
  printf("%-20s%s\n",
  (ref($what) ? $x : `cat $what`) =
s/\0/\\0/gr,
  $what);
  }
}'
--- mode "+<"
truncate SCALAR(0x15e1970)​: Bad file descriptor at -e line 16.
1234567890XYZ SCALAR(0x15e1970)
12345\0\0\0\0\0XYZ tmp.dat
--- mode ">"
truncate SCALAR(0x15e1970)​: Bad file descriptor at -e line 16.
1234567890XYZ SCALAR(0x15e1970)
12345\0\0\0\0\0XYZ tmp.dat
--- mode "+>"
truncate SCALAR(0x15e1970)​: Bad file descriptor at -e line 16.
1234567890XYZ SCALAR(0x15e1970)
12345\0\0\0\0\0XYZ tmp.dat
--- mode ">>"
truncate SCALAR(0x15e1970)​: Bad file descriptor at -e line 16.
1234567890XYZ SCALAR(0x15e1970)
12345XYZ tmp.dat
--- mode "+>>"
truncate SCALAR(0x15e1970)​: Bad file descriptor at -e line 16.
1234567890XYZ SCALAR(0x15e1970)
12345XYZ tmp.dat
[cm@​cos7 ~/perl]$

This is apparently a long-standing limitation (it gets a mention e.g.
in
https://www.effectiveperlprogramming.com/2010/11/dont-modify-scalars-
connected-to-string-filehandles/).

As limitations go however, it is a weird one. Any chance that it might
get lifted (or justified and documented) ?

It should be possible to implement by adding a truncate entry to PerlIO_funcs (perliol.h)

A worthwhile endeavor by any means ;-)

I take it then that there is no hope of being able to fix the problem
for older perls with a CPAN module ?

Tony

---
via perlbug​: queue​: perl5 status​: open
https://rt-archive.perl.org/perl5/Ticket/Display.html?id=131544

@haarg
Copy link
Contributor

haarg commented May 2, 2022

#8572

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants