#!/usr/bin/perl
'di';
'ig00';
# Perl script here
# ...
# end of perl script
# These next few lines are legal in both Perl and nroff.
.00;
'di \" finish diversion--previous line must be blank
.nr nr 0-1 \" fake up transition to first page again
.nr % 0 \" start at page 1
'; __END__ ##### From here on it's a standard manual page #####
This breaks under groff with all sorts of errors which I don't understand
because I don't really know roff (I'm trying to learn, but it's low
priority.) Could someone tell me what needs to be changed to make this
work under groff?
--
Jurgen Botz, jb...@mtholyoke.edu | ``Accountability is the price of openness''
South Hadley, MA, USA | - Daniel Geer
--
Frank
Here's an example of the problem that Botz mentions. Running it
off with `groff -C -man h2ph.1' (groff 1.08) yields the following error
messages. Perhaps these should be downgraded to warnings.
E.g I think traditional troff treats `\$$' as if it were the empty string.
h2ph.1:66: bad argument name `$'
h2ph.1:78: a space character is not allowed in an escape name
h2ph.1:150: bad argument name `$'
----- start of h2ph.1 -----
#!/opt/reb/bin/perl
'di';
'ig00';
$perlincl = '/opt/reb/lib/perl';
chdir '/usr/include' || die "Can't cd /usr/include";
@isatype = split(' ',<<END);
char uchar u_char
short ushort u_short
int uint u_int
long ulong u_long
FILE
END
@isatype{@isatype} = (1) x @isatype;
@ARGV = ('-') unless @ARGV;
foreach $file (@ARGV) {
if ($file eq '-') {
open(IN, "-");
open(OUT, ">-");
}
else {
($outfile = $file) =~ s/\.h$/.ph/ || next;
print "$file -> $outfile\n";
if ($file =~ m|^(.*)/|) {
$dir = $1;
if (!-d "$perlincl/$dir") {
mkdir("$perlincl/$dir",0777);
}
}
open(IN,"$file") || ((warn "Can't open $file: $!\n"),next);
open(OUT,">$perlincl/$outfile") || die "Can't create $outfile: $!\n";
}
while (<IN>) {
chop;
while (/\\$/) {
chop;
$_ .= <IN>;
chop;
}
if (s:/\*:\200:g) {
s:\*/:\201:g;
s/\200[^\201]*\201//g; # delete single line comments
if (s/\200.*//) { # begin multi-line comment?
$_ .= '/*';
$_ .= <IN>;
redo;
}
}
if (s/^#\s*//) {
if (s/^define\s+(\w+)//) {
$name = $1;
$new = '';
s/\s+$//;
if (s/^\(([\w,\s]*)\)//) {
$args = $1;
if ($args ne '') {
foreach $arg (split(/,\s*/,$args)) {
$arg =~ s/^\s*([^\s].*[^\s])\s*$/$1/;
$curargs{$arg} = 1;
}
$args =~ s/\b(\w)/\$$1/g;
$args = "local($args) = \@_;\n$t ";
}
s/^\s+//;
do expr();
$new =~ s/(["\\])/\\$1/g;
if ($t ne '') {
$new =~ s/(['\\])/\\$1/g;
print OUT $t,
"eval 'sub $name {\n$t ${args}eval \"$new\";\n$t}';\n";
}
else {
print OUT "sub $name {\n ${args}eval \"$new\";\n}\n";
}
%curargs = ();
}
else {
s/^\s+//;
do expr();
$new = 1 if $new eq '';
if ($t ne '') {
$new =~ s/(['\\])/\\$1/g;
print OUT $t,"eval 'sub $name {",$new,";}';\n";
}
else {
print OUT $t,"sub $name {",$new,";}\n";
}
}
}
elsif (/^include\s+<(.*)>/) {
($incl = $1) =~ s/\.h$/.ph/;
print OUT $t,"require '$incl';\n";
}
elsif (/^ifdef\s+(\w+)/) {
print OUT $t,"if (defined &$1) {\n";
$tab += 4;
$t = "\t" x ($tab / 8) . ' ' x ($tab % 8);
}
elsif (/^ifndef\s+(\w+)/) {
print OUT $t,"if (!defined &$1) {\n";
$tab += 4;
$t = "\t" x ($tab / 8) . ' ' x ($tab % 8);
}
elsif (s/^if\s+//) {
$new = '';
do expr();
print OUT $t,"if ($new) {\n";
$tab += 4;
$t = "\t" x ($tab / 8) . ' ' x ($tab % 8);
}
elsif (s/^elif\s+//) {
$new = '';
do expr();
$tab -= 4;
$t = "\t" x ($tab / 8) . ' ' x ($tab % 8);
print OUT $t,"}\n${t}elsif ($new) {\n";
$tab += 4;
$t = "\t" x ($tab / 8) . ' ' x ($tab % 8);
}
elsif (/^else/) {
$tab -= 4;
$t = "\t" x ($tab / 8) . ' ' x ($tab % 8);
print OUT $t,"}\n${t}else {\n";
$tab += 4;
$t = "\t" x ($tab / 8) . ' ' x ($tab % 8);
}
elsif (/^endif/) {
$tab -= 4;
$t = "\t" x ($tab / 8) . ' ' x ($tab % 8);
print OUT $t,"}\n";
}
}
}
print OUT "1;\n";
}
sub expr {
while ($_ ne '') {
s/^(\s+)// && do {$new .= ' '; next;};
s/^(0x[0-9a-fA-F]+)// && do {$new .= $1; next;};
s/^(\d+)// && do {$new .= $1; next;};
s/^("(\\"|[^"])*")// && do {$new .= $1; next;};
s/^'((\\"|[^"])*)'// && do {
if ($curargs{$1}) {
$new .= "ord('\$$1')";
}
else {
$new .= "ord('$1')";
}
next;
};
s/^sizeof\s*\(([^)]+)\)/{$1}/ && do {
$new .= '$sizeof';
next;
};
s/^([_a-zA-Z]\w*)// && do {
$id = $1;
if ($id eq 'struct') {
s/^\s+(\w+)//;
$id .= ' ' . $1;
$isatype{$id} = 1;
}
elsif ($id eq 'unsigned') {
s/^\s+(\w+)//;
$id .= ' ' . $1;
$isatype{$id} = 1;
}
if ($curargs{$id}) {
$new .= '$' . $id;
}
elsif ($id eq 'defined') {
$new .= 'defined';
}
elsif (/^\(/) {
s/^\((\w),/("$1",/ if $id =~ /^_IO[WR]*$/i; # cheat
$new .= " &$id";
}
elsif ($isatype{$id}) {
if ($new =~ /{\s*$/) {
$new .= "'$id'";
}
elsif ($new =~ /\(\s*$/ && /^[\s*]*\)/) {
$new =~ s/\(\s*$//;
s/^[\s*]*\)//;
}
else {
$new .= $id;
}
}
else {
$new .= ' &' . $id;
}
next;
};
s/^(.)// && do {$new .= $1; next;};
}
}
##############################################################################
# These next few lines are legal in both Perl and nroff.
.00; # finish .ig
'di \" finish diversion--previous line must be blank
.nr nl 0-1 \" fake up transition to first page again
.nr % 0 \" start at page 1
'; __END__ ############# From here on it's a standard manual page ############
.TH H2PH 1 "August 8, 1990"
.AT 3
.SH NAME
h2ph \- convert .h C header files to .ph Perl header files
.SH SYNOPSIS
.B h2ph [headerfiles]
.SH DESCRIPTION
.I h2ph
converts any C header files specified to the corresponding Perl header file
format.
It is most easily run while in /usr/include:
.nf
cd /usr/include; h2ph * sys/*
.fi
If run with no arguments, filters standard input to standard output.
.SH ENVIRONMENT
No environment variables are used.
.SH FILES
/usr/include/*.h
.br
/usr/include/sys/*.h
.br
etc.
.SH AUTHOR
Larry Wall
.SH "SEE ALSO"
perl(1)
.SH DIAGNOSTICS
The usual warnings if it can't read or write the files involved.
.SH BUGS
Doesn't construct the %sizeof array for you.
.PP
It doesn't handle all C constructs, but it does attempt to isolate
definitions inside evals so that you can get at the definitions
that it can translate.
.PP
It's only intended as a rough tool.
You may need to dicker with the files produced.
.ex
----- end of h2ph.1 -----
It relies mainly on .ig to ignore the input. I've turned errors that
occur while ignoring input into warnings (`ignored' input is subject
to copy mode interpretation). This problem could be avoided if
wrapman turned escapes off (with .eo) while ignoring input. Here's a
patch:
*** input.cc.~52~ Thu May 27 23:29:49 1993
--- input.cc Tue Aug 10 10:23:29 1993
***************
*** 74,79 ****
--- 74,80 ----
static int warning_mask = DEFAULT_WARNING_MASK;
static int inhibit_errors = 0;
+ static int ignoring = 0;
static void enable_warning(const char *);
static void disable_warning(const char *);
***************
*** 87,92 ****
--- 88,98 ----
int tcommand_flag = 0;
static int get_copy(node**, int = 0);
+ static void copy_mode_error(const char *,
+ const errarg & = empty_errarg,
+ const errarg & = empty_errarg,
+ const errarg & = empty_errarg);
+
static symbol read_escape_name();
static void interpolate_string(symbol);
static void interpolate_macro(symbol);
***************
*** 598,604 ****
int c = get_copy(NULL);
switch (c) {
case EOF:
! error("end of input in escape name");
return '\0';
default:
if (!illegal_input_char(c))
--- 604,610 ----
int c = get_copy(NULL);
switch (c) {
case EOF:
! copy_mode_error("end of input in escape name");
return '\0';
default:
if (!illegal_input_char(c))
***************
*** 609,615 ****
case '\t':
case '\001':
case '\b':
! error("%1 is not allowed in an escape name", input_char_description(c));
return '\0';
}
return c;
--- 615,622 ----
case '\t':
case '\001':
case '\b':
! copy_mode_error("%1 is not allowed in an escape name",
! input_char_description(c));
return '\0';
}
return c;
***************
*** 664,670 ****
buf[i] = 0;
if (buf == abuf) {
if (i == 0) {
! error("empty escape name");
return NULL_SYMBOL;
}
return symbol(abuf);
--- 671,677 ----
buf[i] = 0;
if (buf == abuf) {
if (i == 0) {
! copy_mode_error("empty escape name");
return NULL_SYMBOL;
}
return symbol(abuf);
***************
*** 3098,3104 ****
{
const char *s = nm.contents();
if (!s || *s == '\0')
! error("missing argument name");
else if (s[1] == 0 && csdigit(s[0]))
input_stack::push(input_stack::get_arg(s[0] - '0'));
else if (s[0] == '*' && s[1] == '\0') {
--- 3105,3111 ----
{
const char *s = nm.contents();
if (!s || *s == '\0')
! copy_mode_error("missing argument name");
else if (s[1] == 0 && csdigit(s[0]))
input_stack::push(input_stack::get_arg(s[0] - '0'));
else if (s[0] == '*' && s[1] == '\0') {
***************
*** 3119,3125 ****
for (const char *p = s; *p && csdigit(*p); p++)
;
if (*p)
! error("bad argument name `%1'", s);
else
input_stack::push(input_stack::get_arg(atoi(s)));
}
--- 3126,3132 ----
for (const char *p = s; *p && csdigit(*p); p++)
;
if (*p)
! copy_mode_error("bad argument name `%1'", s);
else
input_stack::push(input_stack::get_arg(atoi(s)));
}
***************
*** 3274,3281 ****
}
*mm = mac;
}
! if (term != dot_symbol)
interpolate_macro(term);
else
skip_line();
return;
--- 3281,3290 ----
}
*mm = mac;
}
! if (term != dot_symbol) {
! ignoring = 0;
interpolate_macro(term);
+ }
else
skip_line();
return;
***************
*** 3329,3335 ****
--- 3338,3346 ----
void ignore()
{
+ ignoring = 1;
do_define_macro(DEFINE_IGNORE);
+ ignoring = 0;
}
void remove_macro()
***************
*** 5636,5641 ****
--- 5647,5653 ----
"di", WARN_DI,
"mac", WARN_MAC,
"reg", WARN_REG,
+ "ig", WARN_IG,
"all", WARN_TOTAL & ~(WARN_DI | WARN_MAC | WARN_REG),
"w", WARN_TOTAL,
"default", DEFAULT_WARNING_MASK,
***************
*** 5668,5673 ****
--- 5680,5702 ----
else
error("unknown warning `%1'", name);
}
+
+ static void copy_mode_error(const char *format,
+ const errarg &arg1,
+ const errarg &arg2,
+ const errarg &arg3)
+ {
+ if (ignoring) {
+ const char prefix[] = "(in ignored input) ";
+ char *s = new char[sizeof(prefix) + strlen(s)];
+ strcpy(s, prefix);
+ strcat(s, format);
+ warning(WARN_IG, s, arg1, arg2, arg3);
+ a_delete s;
+ }
+ else
+ error(format, arg1, arg2, arg3);
+ }
enum error_type { WARNING, ERROR, FATAL };
*** troff.h.~7~ Wed Nov 25 16:48:00 1992
--- troff.h Tue Aug 10 10:13:58 1993
***************
*** 76,86 ****
WARN_INPUT = 040000,
WARN_ESCAPE = 0100000,
WARN_SPACE = 0200000,
! WARN_FONT = 0400000
// change WARN_TOTAL if you add more warning types
};
! const int WARN_TOTAL = 0777777;
int warning(warning_type, const char *,
const errarg & = empty_errarg,
--- 76,87 ----
WARN_INPUT = 040000,
WARN_ESCAPE = 0100000,
WARN_SPACE = 0200000,
! WARN_FONT = 0400000,
! WARN_IG = 01000000
// change WARN_TOTAL if you add more warning types
};
! const int WARN_TOTAL = 01777777;
int warning(warning_type, const char *,
const errarg & = empty_errarg,