Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

groff & (Perl) wrapman

2 views
Skip to first unread message

Jurgen Botz

unread,
Aug 9, 1993, 12:01:26 PM8/9/93
to
I just found out (by trying) that the Perl "wrapman" mechanism doesn't
seem to work with groff. This mechanism is supposed to allow appending
the man page for a script to the script itself, so that one only need
place a symbolic link to the script in the appropriate man directory.
It works by wrapping the Perl program with the following lines...

#!/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 Wortner

unread,
Aug 9, 1993, 2:11:07 PM8/9/93
to
Wrapman assumes that troff directives are no more than two characters long.
This isn't true under GROFF. Try giving groff the "-C" flag to force
compatibility with normal troff. That should help somewhat.

--

Frank

Paul Eggert

unread,
Aug 9, 1993, 2:52:14 PM8/9/93
to
Perl's `wrapman' relies on .di to comment out arbitrary sections of
troff input, but this doesn't really work with troff when the diverted
input contains escape sequences that have errors or side effects
(this is much like trying to use `#if 0' to comment out arbitrary text
in an ANSI C program -- it doesn't work in general). However, if you use
`groff -C' and ignore the error messages, you should be able to run off
the manual pages.

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 -----

James Clark

unread,
Aug 11, 1993, 7:01:57 AM8/11/93
to
> Date: Mon, 9 Aug 93 10:46:23 PDT
> From: Paul Eggert <egg...@knee.twinsun.com>

>
> Perl's `wrapman' relies on .di to comment out arbitrary sections of
> troff input, but this doesn't really work with troff when the diverted
> input contains escape sequences that have errors or side effects

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,


0 new messages