in reply to Re: Don't understand why can't get 'size' assoc w/STDOUT
in thread Don't understand why can't get 'size' assoc w/STDOUT

Your program (and your complete 'overkill' solution) led me to finding the solution -- as I took you overkill solution and stripped out the C and converted it to perl...and that finally hilighted 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. ioctl doesnt' 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 ioctl 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...

So *thank you* 'tchrist', for writing your example with *correct code*, that allowed me to find the difference between working and not, and also, thank-you for answering the question I was asking rather than trying to get me to use 'something else'.

  • Comment on Re^2: Don't understand why can't get 'size' assoc w/STDOUT

Replies are listed 'Best First'.
Re^3: Don't understand why can't get 'size' assoc w/STDOUT
by tchrist (Pilgrim) on Apr 18, 2011 at 02:33 UTC
    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.

Re^3: Don't understand why can't get 'size' assoc w/STDOUT
by tchrist (Pilgrim) on Apr 18, 2011 at 14:10 UTC
    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?
      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.