in reply to Re: Re: Re: Unexpected file test (-f) result on Windows in thread Unexpected file test (-f) result on Windows
BrowserUK wrote:
Update: FWIW, this "undocumented feature" is deep within the OS, using '<' in the argument to FindFirstFile/FindNextFile apis also treats it the same way as '*'. It appears that this is the only undocumented character that exhibits this behaviour. Weirdness indeed.
Very strange indeed. As you say, none of the other Windows 'glob' or redirection characters exhibit this peculilar behaviour in this context.
open FILE, ">testfile.log" or die "Unable to create file: $!";
close FILE;
if (! -f "*estfile.log") {
print STDERR "Not a file *.\n";
}
if (! -f "|testfile.log") {
print STDERR "Not a file |.\n";
}
if (! -f "?estfile.log") {
print STDERR "Not a file ?.\n";
}
if (! -f "<testfile.log") {
print STDERR "Not a file <.\n";
}
if (! -f ">>testfile.log") {
print STDERR "Not a file >>.\n";
}
exit;
__END__
Not a file *.
Not a file |.
Not a file ?.
Not a file >>.
What I don't understand is why this is effectively performing a directory search in order to test one filename! Does Windows not have the equivalent of the Unix stat() system call? The -f operator should just be calling the perl stat function. Does anyone with a better understanding of Win32 Perl than I understand what is happening here?
(Should I fall back to matching the filename with a regex?)
Cheers, -- Dave :-)
$q=[split+qr,,,q,~swmi,.$,],+s.$.Em~w^,,.,s,.,$&&$$q[pos],eg,print
Re: Re: Re: Re: Re: Unexpected file test (-f) result on Windows
by BrowserUk (Patriarch) on Sep 20, 2003 at 02:30 UTC
|
Tracking this stuff through the perl sources is a nightmare.
- The -f is mapped to a function Perl_pp_ftis in opcode.h (for FileTestIs (a file) perhaps?).
- Which is mapped to lib\core\embed.h:#define pp_ftis Perl_pp_ftis
- Which is implemented in terms of my_stat() in pp_sys.c
PP(pp_ftis)
{
I32 result = my_stat();
dSP;
if (result < 0)
RETPUSHUNDEF;
RETPUSHYES;
}
- Which is mapped to embed.h:3209:#define my_stat() Perl_my_stat(aTHX)
- Perl_my_stat() is implemented in doio.c in terms of PerlLIO_fstat()
- Which is mapped to iperlsys.h:734:#define PerlLIO_fstat(fd, buf) Fstat((fd), (buf))
- Which gets mapped to lib\core\dosish.h:133:#define Fstat(fd,bufptr) fstat((fd),(bufptr))
- Which is a c-runtime api.
However, fstat() takes a file descriptor (fd), which implies an open file handle...Should have noticed that earlier. Track back to where the calls are not defined in terms of a file descriptor and were back at doio.c. Sure enough, follow the other path in Perl_my_stat()and we find it calls PerlLIO_stat()
- Which maps to lib\core\iperlsys.h:739:#define PerlLIO_stat(name, buf) Stat((name), (buf))
- Which is mapped to dosish.h:142:# define Stat(fname,bufptr) stat((fname),(bufptr))
- Then you have to move over to the C-runtime headers, which I don't have for MSVC++ (as used by Active State), so I can't bore you with those details, but suffice it to say, stat() ends up getting mapped to _stat64() as AS build with large file support which means they need the version of stat that can handle filesizes >32-bit.
Dissassembling the code in MSVCRT.dll:_stat64 we find this
The salient point here is the call to EXT:KERNEL32.DLL!FindFirstFileA.
You asked: ...why this is effectively performing a directory search in order to test one filename!..... And the answer goes something like this.
When you pass a filespec to stat() or one of its varients, in order to ask the os for information about the file, you need to get an OS 'handle' (an INODE) in unix terms) to that file. You can get one of these by various means, but if you aren't interested in opening the file, then the OS gives you a call to obtain that handle. In Win32 this is FindFirstFile(), I think the equivalent under unix is opendir or maybe it gets translated directly into (ioctl?) calls to the underlying filesystem.
Anyway, the point is that under normal circumstances, calling FFF with a non-wildcard filespec returns a structure containing (almost) all the information required to satisfy the stat() call.
Whilst you may not thinkof stat() as doing a "directory search", it has to have the filesystem search to find the information to fulfil the stat() call, which on unix is in the INODE and so a search is being done, it's just that the API name doesn't reflect this.
The fact that your filespec contains some adornments used for determining the filemode is a Perl thing and not the OS's problem.
That these conflict with an undocumented feature within the OS is ...erm.. unfortunate! I guess that LW and MS came to the same conclusion that '<' & '>' are good candidates for using as meta characters as most sane people are unlikely to embed these in their filenames as they would conflict with their use as redirection metacharacters by CLIs.
In the final analysis, you would have to strip Perls two-arg open metacharacters from the filespec before you passed them to stat(), whichever OS you are on!
Examine what is said, not who speaks.
"Efficiency is intelligent laziness." -David Dunham
"When I'm working on a problem, I never think about beauty. I think only how to solve the problem. But when I have finished, if the solution is not beautiful, I know it is wrong." -Richard Buckminster Fuller
If I understand your problem, I can solve it! Of course, the same can be said for you.
| [reply] [d/l] [select] |
|
Whew! :-)
BrowserUK++! Thank you so much for your in-depth analysis; this strange problem obviously piqued your curiosity even more-so than mine!
It just goes to show that you should never make assumptions about how things actually work when trying to write portable code.
Thanks again.
Cheers, -- Dave :-)
$q=[split+qr,,,q,~swmi,.$,],+s.$.Em~w^,,.,s,.,$&&$$q[pos],eg,print
| [reply] |
|
|