Using fcntl and |= - "Argument .... isn't numeric in bitwise or ..."

J

Jim Cochrane

I'm stumped - trying to get rid of a warning message with code based on
this example snippet from the "Perl Cookbook" book:

use Fcntl;

$lags = "";
fcntl(HANDLE, F_GETFL, $flags) or die "Couldn't get flags: $!\n";
$flags |= O_NONBLOCK;
fcntl(HANDLE, F_SETFL, $flags) or die "Couldn't set flags: $!\n";

(I've typed the above by hand and shortened the error messages - sorry
for any typos.)

I did some searching, including messages about similar problems in this
NG, but failed to find anything relevant.

My code, a short example (included below), produces the warning:

Argument "\0\0\0\0/test1\0\0^P\0\0\0XM-3j^ITLKW \0\0\0^P\0\0\0hM-3..." isn't numeric in bitwise or (|) at ./odd-flags.pl line 13.

This refers to the line:

$flags |= O_NONBLOCK;

I suspect this has something to do with this paragraph from the fcntl
man page:

You don’t have to check for "defined" on the return from
"fcntl". Like "ioctl", it maps a 0 return from the system call
into "0 but true" in Perl. This string is true in boolean con-
text and 0 in numeric context. It is also exempt from the nor-
mal -w warnings on improper numeric conversions.

But the last sentence appears to imply the above warning should not
occur.

FYI:
perl -v

This is perl, v5.8.6 built for i386-linux-thread-multi

Is the perl version I'm using mistakenly producing this warning or have
I missed something? If the former - any recommendations on how to
cleanly suppress the warning. (Turn warnings off just for the
"offending" line?)

Here's my example code:

------------------------------------------------------------------
#!/usr/bin/perl

use strict;
use warnings;

use Fcntl;

my $file;
open $file, '>/tmp/test1';
my $flags = '';
fcntl($file, F_GETFL, $flags) or die "Could not get flags: $!";
print "flags: $flags\n";
$flags |= O_NONBLOCK;
print "flags: $flags\n";
fcntl($file, F_SETFL, $flags) or die "Could not set flags: $!";

fcntl($file, F_GETFL, $flags) or die "Could not get flags: $!";
print "flags: $flags\n";

# Below lines added only to get more info about the problem:
fcntl($file, F_GETFL, $flags) or die "Could not get flags: $!";
print "flags: $flags\n";
$flags |= 8;
print "flags: $flags\n";
fcntl($file, F_SETFL, $flags) or die "Could not set flags: $!";

fcntl($file, F_GETFL, $flags) or die "Could not get flags: $!";
print "flags: $flags\n";

------------------------------------------------------------------

Oddly, it complains about the first |=, but not the 2nd. It produces
this output (sorry about the odd characters):


Argument "\0\0\0\0/test1\0\0^P\0\0\0XM-cp^ITLKW \0\0\0^P\0\0\0hM-c..." isn't numeric in bitwise or (|) at ./odd-flags.pl line 13.
flags: flags: 2048
flags: 2048flags: 2048flags: 2056
flags: 2056


Thanks for any help or pointers on this!

--
 
B

Ben Morrow

Quoth Jim Cochrane said:
I'm stumped - trying to get rid of a warning message with code based on
this example snippet from the "Perl Cookbook" book:

use Fcntl;

$lags = "";
fcntl(HANDLE, F_GETFL, $flags) or die "Couldn't get flags: $!\n";

You need to read you systems fcntl(2) as well as the perldocs; at least
on mine, it says

F_GETFL Get descriptor status flags, as described below (arg is
ignored).

So, contrary to the example in perldoc -f fcntl, you need

my $flags = fcntl(HANDLE, F_GETFL);

Ben
 
J

Jim Cochrane

You need to read you systems fcntl(2) as well as the perldocs; at least
on mine, it says

F_GETFL Get descriptor status flags, as described below (arg is
ignored).

So, contrary to the example in perldoc -f fcntl, you need

my $flags = fcntl(HANDLE, F_GETFL);

This appears to be wrong. It looks like fcntl takes 3 arguments with
F_GETFL, even though the 3rd arg is not passed as a reference. (Trying
it with just 2 args -

$flags = fcntl($handle, F_GETFL) or die "Could not get flags: $!";

produces the error:

Not enough arguments for fcntl at ./exp8.pl line 37, near "F_GETFL) "
)

However, it looks like the answer to my question is that the warning
occurs because the example is based on old code that does not 'use
warnings' and thus does not produce a warning message unless 'use
warnings' is added. I found that instead of doing this, which produced
the warning:

fcntl($file, F_GETFL, $flags) or die "Could not get flags: $!";
$flags |= O_NONBLOCK;
fcntl($file, F_SETFL, $flags) or die "Could not set flags: $!";

doing it this way does not produce a warning:

fcntl($file, F_GETFL, $flags) or die "Could not get flags: $!";
fcntl($file, F_SETFL, $flags | O_NONBLOCK) or die "Could not set flags: $!";

So to answer my own question, the warning is expected and the best way
to get rid of it is to simply use the latter form instead of changing
the value of $flags with |=.


--
 
J

Jim Cochrane

Your problem has nothing at all to do with fcntl

"use strict" and "use warnings" would have shown you the problem.

You've missed the mark and have things pretty much reversed. And you
don't appear to have read my original message very well. I'll summarize
what I found, below.

You've excerpted my typed-in quote from the Perl Cookbook, rather
than the code at the bottom of my message that I was actually running
(and which does 'use strict' and 'use warnings').

[From my original message: "(I've typed the above by hand and shortened
the error messages - sorry for any typos.)" and "My code, a short example
(included below), produces the warning:

Argument "\0\0\0\0/test1\0\0^P\0\0\0XM-3j^ITLKW \0\0\0^P\0\0\0hM-3..."
isn't numeric in bitwise or (|) at ./odd-flags.pl line 13."
]
It's $flags which is undefined. You've set $lags, not $flags (and
setting it to 0 rather than "" would make far more sense).


You aren't even checking the return result from fcntl. You are just
ignoring it completely (you're calling it in a void context, i.e. not
assigning the return value to anything).

If you're going to respond to a message, please read it properly first,
take some time to understand the issue, and do a better job of quoting
from it.

What I found was that the warning message occurs because the 'use
warnings' facility does not like the use of the | operator with the
non-numeric value assigned to the $flags variable by the first fcntl
call. The best solution appears to be to insert a local 'no warnings
"numeric"' line before the "offending" code. (I posted a followup a
couple days ago that incorrectly stated that deleting:

$flags |= O_NONBLOCK;

and instead doing the or in the 2nd fcntl call:

fcntl($file, F_SETFL, $flags | O_NONBLOCK) or die "Could not set flags: $!\n";

got rid of the warning; but it doesn't - I had forgotten to remove the
'no warnings...' line when I tried that.)

For reference, here's my test code posted in my original message:

------------------------------------------------------------------
#!/usr/bin/perl

use strict;
use warnings;

use Fcntl;

my $file;
open $file, '>/tmp/test1';
my $flags = '';
fcntl($file, F_GETFL, $flags) or die "Could not get flags: $!";
print "flags: $flags\n";
$flags |= O_NONBLOCK;
print "flags: $flags\n";
fcntl($file, F_SETFL, $flags) or die "Could not set flags: $!";

fcntl($file, F_GETFL, $flags) or die "Could not get flags: $!";
print "flags: $flags\n";

# Below lines added only to get more info about the problem:
fcntl($file, F_GETFL, $flags) or die "Could not get flags: $!";
print "flags: $flags\n";
$flags |= 8;
print "flags: $flags\n";
fcntl($file, F_SETFL, $flags) or die "Could not set flags: $!";

fcntl($file, F_GETFL, $flags) or die "Could not get flags: $!";
print "flags: $flags\n";
 
J

Jim Cochrane

That's because you did not put $flags on the left hand side of
an equal sign. Both undef and '0' are legal for |=, '' is not.

Duh - I wrote my code based on the example in Perl Cookbook (p. 284).
But I should have noticed that - "$flags = '';" is an empty string, not
a number. I wrongly assumed $flags got its non-numeric value from the
first fcntl call. I need to remember not to take books - even those
with good reputations - as gospel.

Thanks for pointing this out; everyone else who responded missed it.
That's not how its documented in "perldoc -f fcntl".

Here's an example of setting a filehandle named "REMOTE" to be
non-blocking at the system level.

use Fcntl qw(F_GETFL F_SETFL O_NONBLOCK);

$flags = fcntl(REMOTE, F_GETFL, 0)
or die "Can't get flags for the socket: $!\n";

$flags = fcntl(REMOTE, F_SETFL, $flags | O_NONBLOCK)
or die "Can't set flags for the socket: $!\n";

Thanks; another thing wrong with the Cookbook example, although
'fcntl(REMOTE, F_GETFL, $flags)' version appears to work (I think -
don't have time to test it right now); but the form documented in perldoc
is cleaner.
That last line in the docs is misleading. It should be:

$value = fcntl(REMOTE, F_SETFL, $flags | O_NONBLOCK)
or die "Can't set flags for the socket: $!\n";

You mean the result is a status value, not a new 'flags' value. OK.
#!/usr/bin/perl
use strict;
use warnings;
use Fcntl;

my $file;
open $file, '>/tmp/test1';
printf "0: O_NONBLOCK = %d = 0x%X, file = %s\n",O_NONBLOCK,O_NONBLOCK,$file;

my $flags;
$flags = fcntl($file, F_GETFL, 0) or die "Could not get flags: $!";
printf "1: flags = 0x%08X\n",$flags;
$flags |= O_NONBLOCK;
printf "2: flags = 0x%08X\n",$flags;
$_ = fcntl($file, F_SETFL, $flags) or die "Could not set flags: $!";
printf "3: value as string = %s\n",$_;

$flags = fcntl($file, F_GETFL, 0) or die "Could not get flags: $!";
printf "4: flags = 0x%08X\n",$flags;
$flags |= 8;
printf "5: flags = 0x%08X\n",$flags;
$_ = fcntl($file, F_SETFL, $flags) or die "Could not set flags: $!";
printf "6: value as string = %s\n",$_;

$flags = fcntl($file, F_GETFL, $flags) or die "Could not get flags: $!";
printf "7: flags = 0x%08X\n",$flags;
print "Setting bit for 8 is ",($flags&8 ? 'supported' : 'not implemented'),
" on $^O\n";

########
0: O_NONBLOCK = 2048 = 0x800, file = GLOB(0x8770c20)
1: flags = 0x00008001
2: flags = 0x00008801
3: value as string = 0 but true
4: flags = 0x00008801
5: flags = 0x00008809
6: value as string = 0 but true
7: flags = 0x00008801
Setting bit for 8 is not implemented on linux
Cool.


Works as documented.

-Joe

Thanks

--
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,764
Messages
2,569,564
Members
45,039
Latest member
CasimiraVa

Latest Threads

Top