Perl Cookbook

Perl CookbookSearch this book
Previous: 12.13. Referring to Packages IndirectlyChapter 12
Packages, Libraries, and Modules
Next: 12.15. Using h2xs to Make a Module with C Code
 

12.14. Using h2ph to Translate C #include Files

Problem

Someone gave you code that generates the bizarre error message:

Can't locate sys/syscall.ph in @INC (did you run h2ph?)
(@INC contains: /usr/lib/perl5/i686-linux/5.00404 /usr/lib/perl5
/usr/lib/perl5/site_perl/i686-linux /usr/lib/perl5/site_perl .)
at some_program line 7.

You want to know what it means and how to fix it.

Solution

Get your system administrator to do this, running as the superuser:

% cd /usr/include; h2ph sys/syscall.h

However, most include files require other include files, which means you should probably just translate them all:

% cd /usr/include; h2ph *.h */*.h

If that reports too many filenames or misses some that are more deeply nested, try this instead:

% cd /usr/include; find . -name '*.h' -print | xargs h2ph

Discussion

A file whose name ends in ".ph" has been created by the h2ph tool, which translates C preprocessor directives from C #include files into Perl. The goal is to allow Perl code to access the same constants as C code. The h2xs tool is a better approach in most cases because it provides compiled C code for your modules, not Perl code simulating C code. However, using h2xs requires a lot more programming savvy (at least, for accessing C code) than h2ph does.

When h2ph's translation process works, it's wonderful. When it doesn't, you're probably out of luck. As system architectures and include files become more complex, h2ph fails more frequently. If you're lucky, the constants you need are already in the Fcntl, Socket, or POSIX modules. The POSIX module implements constants from sys/file.h, sys/errno.h, and sys/wait.h, among others. It also allows fancy tty handling, as described in Recipe 15.8.

So what can you do with these .ph files? Here are a few examples. The first uses the pessimally non-portable syscall function to access your operating system's gettimeofday system call. This implements the FineTime module described in Recipe 12.11.

# file FineTime.pm
package main;
require 'sys/syscall.ph';
die "No SYS_gettimeofday in sys/syscall.ph"
    unless defined &SYS_gettimeofday;

package FineTime;
    use strict;
require Exporter;
use vars qw(@ISA @EXPORT_OK);
@ISA = qw(Exporter);
@EXPORT_OK = qw(time);

sub time() {
    my $tv = pack("LL", ());  # presize buffer to two longs
    syscall(&main::SYS_gettimeofday, $tv, undef) >= 0
        or die "gettimeofday: $!";
    my($seconds, $microseconds) = unpack("LL", $tv);
    return $seconds + ($microseconds / 1_000_000);
}

1;

If you are forced to require an old-style .pl or .ph file, do so from the main package (package main in the preceding code). These old libraries always put their symbols in the current package, and main serves as a reasonable rendezvous point. To use a symbol, use its fully qualified name, as we did with main::SYS_gettimeofday.

The sys/ioctl.ph file, if you can get it to build on your system, is the gateway to your system's idiosyncratic I/O functions through the ioctl function. One such function is the TIOCSTI ioctl, shown in Example 12.1. That abbreviation stands for "terminal I/O control, simulate terminal input." On systems that implement this function, it will push one character into your device stream so that the next time any process reads from that device, it gets the character you put there.

Example 12.1: jam

#!/usr/bin/perl -w
# jam - stuff characters down STDIN's throat
require 'sys/ioctl.ph';
die "no TIOCSTI" unless defined &TIOCSTI;
sub jam {
    local $SIG{TTOU} = "IGNORE"; # "Stopped for tty output"
    local *TTY;  # make local filehandle
    open(TTY, "+</dev/tty")                 or die "no tty: $!";
    for (split(//, $_[0])) {
        ioctl(TTY, &TIOCSTI, $_)            or die "bad TIOCSTI: $!";
    }
    close(TTY);
}
jam("@ARGV\n");

Since sys/ioctl.h translation is so dodgy, you'll probably have to run this C program to get your TIOCSTI value.

% cat > tio.c <<EOF && cc tio.c && a.out
#include <sys/ioctl.h>
main() { printf("%#08x\n", TIOCSTI); }
EOF
0x005412

Another popular use for ioctl is for figuring out your current window size in rows and columns, and maybe even in pixels. This is shown in Example 12.2.

Example 12.2: winsz

#!/usr/bin/perl
# winsz - find x and y for chars and pixels
require 'sys/ioctl.ph';
die "no TIOCGWINSZ " unless defined &TIOCGWINSZ;
open(TTY, "+</dev/tty")                     or die "No tty: $!";
unless (ioctl(TTY, &TIOCGWINSZ, $winsize='')) {
    die sprintf "$0: ioctl TIOCGWINSZ (%08x: $!)\n", &TIOCGWINSZ;
}
($row, $col, $xpixel, $ypixel) = unpack('S4', $winsize);
print "(row,col) = ($row,$col)";
print "  (xpixel,ypixel) = ($xpixel,$ypixel)" if $xpixel || $ypixel;
print "\n";

As you see, as soon as you start playing with .ph files, unpacking binary data, and calling syscall and ioctl, you need to know about the C APIs that Perl normally hides. The only other thing that requires this much C knowledge is using the XS interface. Some suggest you should resist the temptation to descend into such unportable convolutions. Others feel that the demands put upon the trenchworkers are such that they must be forgiven these desperate measures.

Fortunately, less fragile mechanisms are increasingly available. CPAN modules for most of these functions now exist, which should theoretically prove more robust than sourcing .ph files.

See Also

h2ph (1); the instructions on running h2ph in the INSTALL file from the perl source distribution; the syscall and ioctl functions in Chapter 3 of Programming Perl and in perlmod (1); Recipe 12.15


Previous: 12.13. Referring to Packages IndirectlyPerl CookbookNext: 12.15. Using h2xs to Make a Module with C Code
12.13. Referring to Packages IndirectlyBook Index12.15. Using h2xs to Make a Module with C Code