in reply to Re: readdir() on a sysopen() handle?
in thread readdir() on a sysopen() handle?

As you wrote this I was actually fiddling with exactly that :-) The relevant Perl source appears to be pp_open_dir in pp_sys.c, which uses the IoDIRP macro, which apparently accesses the DIR * xiou_dirp slot of struct xpvio, but I can't seem to find any more documentation on it.

Disclaimer: I am not an XS expert, I can't guarantee that the following is entirely correct! I got some of this from the Inline::C::Cookbook, a bit of research in perlapi, and a bit of fiddling...

myreaddir does all of the work of opening and reading the directory in C, returning a Perl list, while _xs_myfdopendir with the Perl wrapper myfdopendir attempts to be a custom opendir.

use warnings; use strict; use Inline C => <<'END_OF_C'; void myreaddir(SV* sv_dirn) { Inline_Stack_Vars; Inline_Stack_Reset; int fd = open( SvPVx(sv_dirn, PL_na), O_RDONLY|O_DIRECTORY|O_NOFOLLOW); if (fd<0) Inline_Stack_Return(0); DIR* dir = fdopendir(fd); if (dir==NULL) Inline_Stack_Return(0); struct dirent *dp; while ( (dp=readdir(dir)) != NULL ) Inline_Stack_Push(sv_2mortal( newSVpvf("%s", dp->d_name) )); if( closedir(dir)!=0 ) Inline_Stack_Return(0); Inline_Stack_Done; } int _xs_myfdopendir(SV* sv_dirn, SV* sv_hnd) { int fd = open( SvPVx(sv_dirn, PL_na), O_RDONLY|O_DIRECTORY|O_NOFOLLOW); if (fd<0) return 0; DIR* dir = fdopendir(fd); if (dir==NULL) return 0; IoDIRP(sv_2io(sv_hnd)) = dir; return 1; } END_OF_C use Symbol qw/geniosym/; use File::Spec; sub myfdopendir { return unless _xs_myfdopendir( $_[0]//File::Spec->curdir, my $dh=geniosym ); return $dh; } use Data::Dump; my @x = myreaddir('/tmp') or die $!; dd @x; my $dh = myfdopendir('/tmp') or die $!; dd readdir $dh; closedir $dh or die $!;

Update: A couple of Perl modules that use XS to read directories, in particular the first one's readdir_hashref looks like it could be modified fairly simply: ReadDir, IO-Dirent, PerlIO-Util

Replies are listed 'Best First'.
Re^3: readdir() on a sysopen() handle?
by ikegami (Patriarch) on Aug 21, 2017 at 04:20 UTC

     or die $! is wrong in my @x = myreaddir('/tmp') or die $!; because an empty list doesn't necessarily denote an error.

      Yes, that's a very good point, thanks. At least on Linux (and probably Windows), I believe even empty directories should return the list ('..', '.'), but your point that the error checking is lacking is still correct. Anyway, myfdopendir is probably better in that respect, I was just less sure on whether I got code for the Perl internals right on that one.

        I didn't think about . and ...

        That said, I believe the root directory an empty drive can return nothing on Windows. (. and .. is not returned for the root of some drives on Windows.)