Make fileno() builtin work on directory handles
authorAaron Crane <arc@cpan.org>
Fri, 17 Oct 2014 15:59:22 +0000 (16:59 +0100)
committerAaron Crane <arc@cpan.org>
Sun, 7 Dec 2014 12:30:51 +0000 (12:30 +0000)
This requires OS support in the form of either the dirfd() function, or a
dd_fd member in the DIR struct. POSIX.1-2008 specifies the former. If
neither is available, fileno() on a directory handle will return undef and
set errno.

pod/perldelta.pod
pod/perlfunc.pod
pp_sys.c
t/io/perlio.t

index 349f4cd..ff94fe0 100644 (file)
@@ -27,6 +27,18 @@ here, but most should go in the L</Performance Enhancements> section.
 
 [ List each enhancement as a =head2 entry ]
 
+=head2 C<fileno> now works on directory handles
+
+When the relevant support is available in the operating system, the
+C<fileno> builtin now works on directory handles, yielding the
+underlying file descriptor in the same way as for filehandles. On
+operating systems without such support, C<fileno> on a directory handle
+continues to return the undefined value, as before, but also sets C<$!> to
+indicate that the operation is not supported.
+
+Currently, this uses either a C<dd_fd> member in the OS C<DIR>
+structure, or a dirfd(3) function as specified by POSIX.1-2008.
+
 =head1 Security
 
 XXX Any security-related notices go here.  In particular, any security
index 5fe4b3d..eb0a4e1 100644 (file)
@@ -2297,6 +2297,12 @@ same underlying descriptor:
             "not have a real file descriptor\n";
     }
 
+The behavior of C<fileno> on a directory handle depends on the operating
+system. On a system with dirfd(3) or similar, C<fileno> on a directory
+handle returns the underlying file descriptor associated with the
+handle; on systems with no such support, it returns the undefined value,
+and sets C<$!> (errno).
+
 =item flock FILEHANDLE,OPERATION
 X<flock> X<lock> X<locking>
 
index 09eead6..4f5b0d4 100644 (file)
--- a/pp_sys.c
+++ b/pp_sys.c
@@ -743,6 +743,22 @@ PP(pp_fileno)
        return tied_method0(SV_CONST(FILENO), SP, MUTABLE_SV(io), mg);
     }
 
+    if (io && IoDIRP(io)) {
+#if defined(HAS_DIRFD) || defined(HAS_DIR_DD_FD)
+        PUSHi(my_dirfd(IoDIRP(io)));
+        RETURN;
+#elif defined(ENOTSUP)
+        errno = ENOTSUP;        /* Operation not supported */
+        RETPUSHUNDEF;
+#elif defined(EOPNOTSUPP)
+        errno = EOPNOTSUPP;     /* Operation not supported on socket */
+        RETPUSHUNDEF;
+#else
+        errno = EINVAL;         /* Invalid argument */
+        RETPUSHUNDEF;
+#endif
+    }
+
     if (!io || !(fp = IoIFP(io))) {
        /* Can't do this because people seem to do things like
           defined(fileno($foo)) to check whether $foo is a valid fh.
index 3987a79..8dfac17 100644 (file)
@@ -6,7 +6,7 @@ BEGIN {
        skip_all_without_perlio();
 }
 
-plan tests => 46;
+plan tests => 48;
 
 use_ok('PerlIO');
 
@@ -113,6 +113,27 @@ ok(close($utffh));
     }
 }
 
+# fileno() for directory handles, on supported platforms
+SKIP: {
+    opendir my $dh, "io"
+        or die "Huh? Can't open directory 'io' containing this file: $!\n";
+    local $! = 0;
+    my $fd = fileno $dh;
+    my $errno = 0 + $!;
+    closedir $dh
+        or die "Huh? Can't close freshly-opened directory handle: $!\n";
+    if ($Config{d_dirfd} || $Config{d_dir_dd_fd}) {
+        ok(defined $fd, "fileno(DIRHANDLE) is defined under dirfd()")
+            or skip("directory fd was undefined", 1);
+        like($fd, qr/\A\d+\z/a,
+             "fileno(DIRHANDLE) yields non-negative int under dirfd()");
+    }
+    else {
+        ok(!defined $fd, "fileno(DIRHANDLE) is undef when no dirfd()");
+        isnt($errno, 0, "fileno(DIRHANDLE) sets errno when no dirfs()");
+    }
+}
+
 sub find_filename {
     my ($fh, @globs) = @_;
     my ($dev, $inode) = stat $fh;