perl-diddler wrote something that — after copyediting and HTML formatting — ran something like:
Your program (and your complete ‘overkill’ solution) led me to finding the solution — as I took your overkill solution and stripped out the C and converted it to Perl, and that finally highlighted what was wrong with my original program. The problem is nothing to do with a “problem with STDOUT” — it’s the **result** word. Changing the line:
my $winsize=0;
to
my $winsize= ' ' x 8;
Solves the problem. It works. (' ' x 8 " =~= your "\0" x 8)
At this point, the problem is clear: the Perl ioctl doesn’t allocate space for the return value — it expects the space to already be there. The ‘Bad address’ error message that comes back is a reference to my having given the C version of the ioctl syscall a bad address for the result buffer.
Once I give it the needed space (for 4 ‘shorts’) — it’s happy!
ARGGGG!!!! I’ve been programming in Perl for too long and forgotten my “C” basics...
Well, yes: it’s always good to do systems programming is C now and then: it reminds you how it’s done. :)
The Perl ioctl function has documentation in the perlfunc manpage that explains what happened to you, albeit perhaps not quite as clearly as one might wish:
SCALAR will be read and/or written depending on the FUNCTION;
a C pointer to the string value of SCALAR will be passed as the third
argument of the actual ioctl call. (If SCALAR has no string value but
does have a numeric value, that value will be passed rather than a pointer
to the string value. To guarantee this to be true, add a 0 to the scalar
before using it.)
As you have (re?)discovered, there is of course no possible way for Perl to know how large a buffer to prepare for whatever ioctl you happen to be calling to write however much data it wants there. Different ioctls need different things, and Perl cannot know one from another. It just makes the syscall and expects you to Do The Right Thing. After that, well — the kernel is certainly not about to allocate memory for you! :) By assigning your variable a value of integer 0, you managed to trick Perl into supplying a mere number to an ioctl that actually needed not a little numeric value but rather a place to write return values into.
And here is an example from Programming Perl: require "sys/ioctl.ph";
$size = pack("L", 0);
ioctl(FH, FIONREAD(), $size)
|| die "Couldn't call ioctl: $!\n";
$size = unpack("L", $size);
Which also shows the need to pre-allocate enough memory for ioctl to write into. I suppose I could make this point clearer. I tend to find that nowadays most people look at me like I’m trying to address them in Homeric Greek if I ever bring up ioctls — or C coding, for that matter.
My so-called “overkill” solution was there to show you the C code needed to get the right structure size on your system so you could make something big enough, and also of course how to get the right value for the ioctl argument that works on your system — they do vary, as my sample runs demonstrated — instead of praying that you have a proper sys/ioctl.ph installed. My first version was just the C one, but then I figured I might as well show you the magical Inline::C version, which is kinda nifty. | [reply] [d/l] [select] |
Please tell me whether this update would have helped make your job easier with ioctl:
=head2 ioctl
ioctl R<FILEHANDLE>, R<FUNCTION>, R<SCALAR>
X<ioctl function>
X<input;using ioctl for>
X<output;using ioctl for>
This function implements the I<ioctl>(2) syscall which controls
I/O. To get the correct function definitions, first you'll
probably have to say:
require "sys/ioctl.ph"; # perhaps /usr/local/lib/perl/sys/ioctl.ph
If I<sys/ioctl.ph> doesn't exist or doesn't have the correct
definitions, you'll have to roll your own based on your C header
files such as I<sys/ioctl.h>. (The Perl distribution includes a
script called I<h2ph> to help you do this, but running it is
nontrivial.) R<SCALAR> will be read or written (or both) depending
on the R<FUNCTION>--a pointer to the string value of R<SCALAR> will
be passed as the third argument of the actual I<ioctl>(2) call. If
R<SCALAR> has no string value but does have a numeric value, that
value will be passed directly rather than a pointer to the string
value. The C<pack> and C<unpack> functions are useful for
manipulating the values of structures used by C<ioctl>. If the
C<ioctl> needs to write data into your R<SCALAR>, it is up to you to
ensure that the string is long enough to hold what needs to be
written, often by initializing it to a dummy value of the correct
size using C<pack>. The following example determines how many
bytes are available for reading using the C<FIONREAD> C<ioctl>:
X<buffer;pre-allocation>
X<unpack function;example of use>
X<ioctl function;example of use>
require "sys/ioctl.ph";
# pre-allocate the right size buffer:
$size = pack("L", 0);
ioctl(FH, FIONREAD(), $size)
|| die "Couldn't call ioctl: $!\n";
$size = unpack("L", $size);
Here is how to detect the current window sizeN<Or rather,
how to get the window size associated with the
STDOUT filehandle.> in rows and columns:
X<window size; determining>
require "sys/ioctl.ph";
# four unsigned shorts of the native size
$template = "S!4";
# pre-allocate the right size buffer:
my $ws = pack($template, ());
ioctl(STDOUT, TIOCGWINSZ(), $ws)
|| die "Couldn't call ioctl: $!";
($rows, $cols, $xpix, $ypix) = unpack($template, $ws);
If I<h2ph> wasn't installed or doesn't work for you, you can I<grep>
the include files by hand or write a small C program to print out
the value. You may also have to look at C code to determine the
stucture template layout and size needed for your system.
X<ioctl function;return value>
X<fcntl function;return value>
X<"0 but true"; special true value>
The return value of C<ioctl> (and C<fcntl>) is as follows:
=begin table picture
=headrow
=row
=cell Syscall Returns
=cell Perl Returns
=bodyrows
=row
=cell C<-1>
=cell C<undef>
=row
=cell C<0>
=cell String "C<0 but true>"
=row
=cell Anything else
=cell That number
=end table
Thus Perl returns true on success and false on failure, yet you
can still easily determine the actual value returned by the
operating system:
$retval = ioctl(...) || -1;
printf "ioctl actually returned %d\n", $retval;
The special string "C<0 but true>" is exempt from warnings
about improper numeric conversions from the B<-w> command-line
option or the C<use warnings> pragma.
Calls to C<ioctl> should not be considered portable. If, say,
you're merely turning off echo once for the whole script, it's
more portable to say:
system "stty -echo"; # Works on most Unix boxen.
Just because you I<can> do something in Perl doesn't mean you
I<ought> to. For still better portability, you might look at the
C<Term::ReadKey> module from CPAN. For almost anything you might want
to use C<ioctl> on, there probably exists a CPAN module that
already does that, and more portably, too, because they usually rope
your system's C compiler into doing the heavy lifting for you.
Is that better? Does it help? | [reply] [d/l] [select] |
I'd have to see it formatted, since just looking at it, it looks alot more complex, but some of that, I think, is formatting, and I'm not that familiar with all the formatting codes.
| [reply] |