On Win32 this works okay, but when the stat is issued on a Linux platform, the POSIX::S_ISDIR($stat1) does not work correctly.

Numeric return values differ between these two platforms-
On Win32, alimited set of possible return values (2):
16895 for a dir and
33206 for a file.

On Linux, depending on permissions, modes are for example:
16877 for a dir,
33188 for a file

I've been so far unable to fully wrap my head around bit operators, but as far as I understand it, POSIX::S_ISDIR should be able to differentiate between a number of possible values, especially these coming from a Linux filesystem. And not only the limited set of Win32 modes... Right? What am I missing?

Comment on POSIX::S_ISDIR() with $stat->mode values from Windows vs. Linux

You are right: The easy way would be to just use -d and -f, as you pointed out, and equal them to my own breed of flags - right. (already tried that and it worked)

But I would like to stay away from introducing my own lingo and instead stick to standards. As of now, I only need the differentiation between file/dir, but in the future I might needed to have more resolution in that field. And starting my own flags now would lead to reinventing the whole "mode" thing in the future...

Those extra octets with (stat())[2] are filetype, sticky bit and others (or as you pose the question exactly the other way: the lower bits are the standard permission bits). Just mask them with and (and please consider using octals for readability, 33206 is more an illness than anything else).

If you've access to a unix box: man 2 stat; man 3 stat.

As you do have access to an ubuntu box: consider grepping the compiler includes (struct stat, field mode_t), they're authorative and quite often eye-opening (even if they tend to be in that wrong, lower language called C. Still that and the syscalls are the base for Perl).

ignore the leftmost d for now, and you've the 3*3 perm bits for user, group and
other. Which probably are the bits accounting for most of the windows to linux difference in stat output.
(please also ignore the output overload of the ls command, which just has to abose
the x bit for things like hinting at the setgid or setuid bit, and worse - that's
doesn't have anything to do with the bitfield from stat()). The 'd' from ls is used
for a filetype of a dir, as with the test commands of test -d in the shell
or Perl's -d. And this filetype is encoded in some of the higher bits returned
by mask.

jakobi's comment, I think, leads into the very core of the problem. The difference between a numeric, a string and octals. And the fact that I still haven't quite figured out the logics behind a bitmask, bit operators and the like.

A bit of pseudo code to illustrate my script, so someone who kwows this stuff better than me can tell me where I need to throw in the oct() function or so to make it work again:

server-side script does a stat()

server-side script returns a HTTP::Response with content in the form "$name\t$size\t$mode\t$nlink\t$ctime\t$atime\t$mtime"

client-side script receives the response content as a string

client-side script does @stat = split(/\t/, $stat); on the string

client-side script does if(POSIX::S_ISDIR(int($stat[1]))){ ... } # int() to make sure its numeric, but something got lost here

5. worked in a test-setup where "server-side" was on Windows and "client-side" was on Windows
5. broke when I migrated the "server-side" to a Debian machine.
(how to encode/decode $mode to keep it intact while represented as a string?)

First of all, I don't see any need for oct().
This is a bitfield - so you might want to do
a perl -e 'printf "0%o\n", 16895' for human
consumption. And if you ever see that on Unix and it
isn't called tmp, you'll probably also want to give the
culprit a lecture about security risks. The readable
form of this number is 040777, with 777 being the
permission bits of read/write/executable for all of
user, group and other (read maybe as 040ugo:) ).

Now let's peek at the includes, pretending that
the 040 wasn't an obvious hint to us for the encoding
of the directory file type.

Might be a useful trick of the trade
for use with more difficult future questions in situations
where Perl offers less or no suitable abstractions or you
need some background to properly make use of them.

The numbers above are octal numbers, for improved readability,
as hinted at by the leading 0.

So you say in Perl $mode & 0777 to restrict the $mode to the
actual file permissions (cf. chmod arguments!). Say $mode & 07 to check the
access of nobody (but remember in the background that there's also filesystem
specific stuff like ACL and Extended Attributes, which can modify
the traditional perms; hopefully somebody alreade made a module for checking
on these headaches :))

To test for a directory w/o POSIX, but just boolean operators
directly. Which is just what the minimal wrapping by the POSIX module does:

warn "a dir\n" if (stat ".")[2] & 040000 == 040000

with 040000 being __S_IFDIR from the includes above. Recognized POSIX::S_IFDIR?

I think that should be enough about background and boolean masking.
Note that instead of and, or, exor you can also left or right-shift
the bits (man perlop: search for &, |, ^, <<, >>).

AFAIS, the missing bits / misunderstandings were:

not printing the values in octal for readability during debugging,

possibly explicit testing of equality of modes as is, without masking
the irrelevant bits

S_IFDIR considers exactly 1 bit, both on Windows and in the Linux VFS. There
is exactly one type of directory on both platforms for all filesystems :).
excluding of the more interesting non-standard abuses of FUSE for now.

If possible however, try the normal perl tests like -d
as already suggested.

An old dirty hack more down my code, where I forgot about it. A line which in a stupid way testes against the two Win32-cases of dir/file... Works on Windows, but can't handle the richer responses from the Debian platform fs.

Thank you very much for the lecture everyone.
And for the monks who feel I wasted their time: they might take comfort from the fact that I, while this thread grew, went through possibly every iteration of oct(), hex(), sprintf("%5o",$mode), $isdir =~ /^40/, there is...

You DO NOT NEED the POSIX macro emulations and those ugly decimal notation of file modes if you just want to test if a name belongs to a directory or a plain file, use perls -X functions, especially -d to test for a directory and -f to test for a plain file.

print "$somename is a real directory" if -d($somename);
print "$somename is a real plain file" if -f($somename);

Note that those function usually test the link target of a symlink, except of course for -l. If you want "real" files or directories and not symlinks, either test explicitly for a symlink or use lstat(). Think twice before insisting on "real" files or directories. Most people expect symlinks to be completely transparent for applications, so don't violate that expectation except for a very good reason (like running with root privileges in a public writeable directory like /tmp).

Alexander talked enough about symlinks that I want to add this caution on both symlinks and hardlinks to the thread:

If you write to existing files other than append-without-backup, and there's the tiniest risk of a user trying to symlink or hardlink that file: explicitely specify how you write out the file. Funny things happen: E.g. sed -i.bak or perl i.bak and that patch on Fedora recently being removed: changing sed's semantics wrt symlinks and surprising everyone.