docs: Generate grammar and option summaries from manpage.
authorMark Wooding <mdw@distorted.org.uk>
Wed, 22 Feb 2006 02:07:00 +0000 (02:07 +0000)
committerMark Wooding <mdw@distorted.org.uk>
Thu, 9 Mar 2006 20:13:57 +0000 (20:13 +0000)
I've been dreaming about this for ages and finally obtained a tuit from
somewhere.  The (rather grungy) script make-manpage extracts the grammar
and option information from the input manpage and spits it out again.

It's slightly surprising how bad a deroffer it can get away with...

.gitignore
Makefile.am
debian/rules
fw.1.in [moved from fw.1 with 89% similarity]
fw.c
grammar [deleted file]
make-manpage [new file with mode: 0755]
mantext.h [new file with mode: 0644]

index 51c0f7e..5108762 100644 (file)
@@ -13,3 +13,4 @@ mkinstalldirs
 depcomp
 autom4te.cache
 
+GRAMMAR
index 40dd33d..2df8226 100644 (file)
@@ -31,12 +31,27 @@ bin_PROGRAMS = fw
 man_MANS = fw.1
 
 EXTRA_DIST = \
-       $(man_MANS) \
+       fw.1.in GRAMMAR \
+       make-manpage \
        debian/changelog debian/copyright debian/control debian/rules
 
+BUILT_SOURCES = mantext.c
+
+$(srcdir)/GRAMMAR: fw.1.in make-manpage
+       perl $(srcdir)/make-manpage text <$(srcdir)/fw.1.in >$@.new
+       mv $@.new $@
+
+fw.1: fw.1.in make-manpage
+       perl $(srcdir)/make-manpage man <$(srcdir)/fw.1.in >$@.new
+       mv $@.new $@
+
+mantext.c: fw.1.in make-manpage
+       perl $(srcdir)/make-manpage c <$(srcdir)/fw.1.in >$@.new
+       mv $@.new $@    
+
 fw_SOURCES = \
-       chan.c conf.c endpt.c fw.c reffd.c scan.c \
-       chan.h conf.h endpt.h fw.h reffd.h scan.h \
+       chan.c conf.c endpt.c fw.c mantext.c reffd.c scan.c \
+       chan.h conf.h endpt.h fw.h mantext.h reffd.h scan.h \
        \
        source.c source.h target.h \
          exec.c file.c socket.c \
@@ -49,4 +64,7 @@ fw_SOURCES = \
        acl.c fattr.c identify.c privconn.c \
        acl.h fattr.h identify.h privconn.h rlimits.h
 
+CLEANFILES = $(BUILT_SOURCES) fw.1
+MAINTAINERCLEANFILES = $(srcdir)/GRAMMAR
+
 ##----- That's all, folks ---------------------------------------------------
index 3970631..21b6cee 100755 (executable)
@@ -23,7 +23,7 @@ binary-arch: install
        dh_testdir -a
        dh_testroot -a
        dh_compress -a
-       dh_installdocs -a grammar
+       dh_installdocs -a GRAMMAR
        dh_strip -a
        dh_shlibdeps -a
        dh_gencontrol -a
diff --git a/fw.1 b/fw.1.in
similarity index 89%
rename from fw.1
rename to fw.1.in
index 51dc460..542e48b 100644 (file)
--- a/fw.1
+++ b/fw.1.in
@@ -59,6 +59,8 @@
 .de GE
 .PP
 ..
+.de GL
+..
 .
 .de OS
 .PP
@@ -69,6 +71,8 @@
 .de OE
 .RE
 ..
+.de OL
+..
 .
 .\" --- Other bits of styling ---
 .
@@ -477,7 +481,7 @@ is good enough for setting global options, and the implicit context
 disambiguates local options.
 .PP
 The following file attribute options are supported:
-.OS "File attribute options (`fattr')"
+.OS "File attributes (`fattr')"
 .IB prefix .fattr.mode
 .RB [ = ]
 .I mode
@@ -495,7 +499,7 @@ and
 .RB ` , '
 do not have to be quoted within the mode string.
 .OE
-.OS "File attribute options (`fattr')"
+.OS "File attributes (`fattr')"
 .IB prefix .fattr.owner
 .RB [ = ]
 .I user
@@ -511,7 +515,7 @@ and
 are accepted in place of
 .BR owner .
 .OE
-.OS "File attribute options (`fattr')"
+.OS "File attributes (`fattr')"
 .IB prefix .fattr.group
 .RB [ = ]
 .I group
@@ -542,7 +546,7 @@ is used as a source, it is set up immediately.
 The syntax of
 .B file
 sources and targets is like this:
-.GS "File sources and targets"
+.GS "File source and target"
 .I source
 ::=
 .I file
@@ -680,7 +684,7 @@ an error is reported.  If
 the new data is appended to the file.
 .OE
 .OS "File options"
-.BR file.fattr.*
+.BR file.fattr. *
 .OD
 The
 .B file
@@ -706,7 +710,7 @@ which is as follows:
 .br
 .I target
 ::=
-exec
+.I exec
 .br
 .I exec
 ::=
@@ -875,7 +879,7 @@ The soft limit cannot be set above the hard limit.
 .OD
 Clears the program's environment.
 .OE
-.PP
+.OS "Exec options"
 .B exec.env.unset
 .I var
 .OD
@@ -997,6 +1001,8 @@ log messages are not generated.
 Address types also provide their own options.
 .
 .SS "The `inet' socket address type"
+.GL "Socket source and target"
+.OL "Socket options"
 The
 .B inet
 address type provides access to TCP ports.  The
@@ -1041,7 +1047,7 @@ source address accepts the following options:
 .OS "Socket options"
 .B socket.inet.source.addr
 .RB [ = ]
-.RR any | \c
+.BR any | \c
 .I addr
 .OD
 Specify the IP address on which to listen for incoming connections.  The
@@ -1078,7 +1084,7 @@ the range 0--1023.
 .OS "Socket options"
 .B socket.inet.dest.addr
 .RB [ = ]
-.RR any | \c
+.BR any | \c
 .I addr
 .OD
 Specify the IP address to bind the local socket to when making an
@@ -1128,6 +1134,8 @@ of the last entry tried.  If there are no entries defined, the default
 is to allow all clients.
 .
 .SS "The `unix' socket address type"
+.GL "Socket source and target"
+.OL "Socket options"
 The
 .B unix
 address type allows access to Unix-domain sockets.  The syntax for
@@ -1227,330 +1235,11 @@ just logs a message about the signal and continues.
 .\"--------------------------------------------------------------------------
 .SH "GRAMMAR SUMMARY"
 .
-.SS "Basic syntax"
-.I file
-::=
-.I empty
-|
-.I file
-.I stmt
-.RB [ ; ]
-.br
-.I stmt
-::=
-.I option-stmt
-|
-.I fw-stmt
-.br
-.I fw-stmt
-::=
-.B fw
-.I source
-.I options
-.RB [ to | \-> ]
-.I target
-.I options
-.br
-.I options
-::=
-.B {
-.I option-seq
-.B }
-.br
-.I option-seq
-::=
-.I empty
-|
-.I option-stmt
-.RB [ ; ]
-.I option-seq
-.
-.SS "Option syntax"
-.I option-stmt
-::=
-.I q-option
-.br
-.I q-option
-::=
-.I option
-.br
-       |
-.I prefix
-.B .\&
-.I q-option
-.br
-       |
-.I prefix
-.B {
-.I option-seq
-.B }
-.br
-.I prefix
-::=
-.I word
-.
-.SS "File source and target"
-.I source
-::=
-.I file
-.br
-.I target
-::=
-.I file
-.br
-.I file
-::=
-.B file
-.RB  [ .\& ]
-.I fspec
-.RB [ ,
-.IR fspec ]
-.br
-.I fspec
-::=
-.I fd-spec
-|
-.I name-spec
-|
-.I null-spec
-.br
-.I fd-spec
-::=
-.RB [[ : ] fd [ : ]]
-.IR number \c
-.RB | stdin | stdout
-.br
-.I name-spec
-::=
-.RB [[ : ] name [ : ]]
-.I file-name
-.br
-.I file-name
-::=
-.I path-seq
-|
-.B [
-.I path-seq
-.B ]
-.br
-.I path-seq
-::=
-.I path-elt
-|
-.I path-seq
-.I path-elt
-.br
-.I path-elt
-::=
-.B /
-|
-.I word
-.br
-.I null-spec
-::=
-.RB [ : ] null [ : ]
-.
-.SS "Exec source and target"
-.I source
-::=
-.I exec
-.br
-.I target
-::=
-exec
-.br
-.I exec
-::=
-.BR exec
-.RB [ .\& ]
-.I cmd-spec
-.br
-.I cmd-spec
-::=
-.I shell-cmd
-|
-.RI [ prog-name ]
-.B [
-.I argv0
-.I arg-seq
-.B ]
-.br
-.I arg-seq
-::=
-.I word
-|
-.I arg-seq
-.I word
-.br
-.I shell-cmd
-::=
-.I word
-.br
-.I argv0
-::=
-.I word
-.
-.SS "Socket source and target"
-.ll +8i
-.I source
-::=
-.I socket-source
-.br
-.I target
-::=
-.I socket-target
-.br
-.I socket-source
-::=
-.RB [ socket [ .\& ]]
-.RB [[ : ] \c
-.IR addr-type \c
-.RB [ : ]]
-.I source-addr
-.br
-.I socket-target
-::=
-.RB [ socket [ .\& ]]
-.RB [[ : ] \c
-.IR addr-type \c
-.RB [ : ]]
-.I target-addr
-.ll -8i
-.PP
-.I inet-source-addr
-::=
-.RB [ port ]
-.I port
-.br
-.I inet-target-addr
-::=
-.I address
-.RB [ : ]
-.I port
-.br
-.I address
-::=
-.I addr-elt
-|
-.I address
-.I addr-elt
-.br
-.I addr-elt
-::=
-.B .\&
-|
-.I word
-.PP
-.I unix-source-addr
-::=
-.I file-name
-.br
-.I unix-target-addr
-::=
-.I file-name
-.
+@@@ grammar
 .\"--------------------------------------------------------------------------
 .SH "OPTION SUMMARY"
 .
-.SS "File attributes (`fattr')"
-.IB prefix .fattr.mode
-.RB [ = ]
-.I mode
-.br
-.IB prefix .fattr.owner
-.RB [ = ]
-.I user
-.br
-.IB prefix .fattr.group
-.RB [ = ]
-.I group
-.
-.SS "File options"
-.B file.create
-.RB [ = ]
-.BR yes | no
-.br
-.B file.open
-.RB [ = ]
-.BR no | truncate | append
-.br
-.BR file.fattr. *
-.
-.SS "Exec options"
-.B exec.logging
-.RB [ = ]
-.BR yes | no
-.br
-.B exec.dir
-.RB [ = ]
-.I file-name
-.br
-.B exec.root
-.RB [ = ]
-.I file-name
-.br
-.B exec.user
-.RB [ = ]
-.I user
-.br
-.B exec.group
-.RB [ = ]
-.I group
-.br
-.BI exec.rlimit. limit \c
-.RB [ .hard | .soft ]
-.RB [ = ]
-.I value
-.br
-.B exec.env.clear
-.br
-.B exec.env.unset
-.I var
-.br
-.BR exec.env. [ set ]
-.I var
-.RB [ = ]
-.I value
-.
-.SS "Socket options"
-.B socket.conn
-.RB [ = ]
-.IR number | \c
-.BR unlimited | one-shot
-.br
-.B socket.listen
-.RB [ = ]
-.I number
-.br
-.B socket.logging
-.RB [ = ]
-.BR yes | no
-.PP
-.BR socket.inet.source. [ allow | deny ]
-.RB [ host ]
-.I addr
-.RB [ /
-.IR addr ]
-.br
-.BR socket.inet.source. [ allow | deny ]
-.B priv-port
-.br
-.B socket.inet.source.addr
-.RB [ = ]
-.BR any | \c
-.I addr
-.br
-.B socket.inet.dest.addr
-.RB [ = ]
-.BR any | \c
-.I addr
-.br
-.B socket.inet.dest.priv-port
-.RB [=]
-.BR yes | no
-.PP
-.BR socket.unix.fattr. *
-.
+@@@ option
 .\"--------------------------------------------------------------------------
 .SH "BUGS"
 .
diff --git a/fw.c b/fw.c
index 6f886e5..f626c6e 100644 (file)
--- a/fw.c
+++ b/fw.c
@@ -61,6 +61,7 @@
 #include "fattr.h"
 #include "file.h"
 #include "fw.h"
+#include "mantext.h"
 #include "privconn.h"
 #include "scan.h"
 #include "socket.h"
@@ -468,100 +469,15 @@ Other options:\n\
 static void grammar(FILE *fp)
 {
   version(fp);
-  pquis(fp, "\n\
-Grammar summary\n\
-\n\
-Basic syntax\n\
-       FILE ::= EMPTY | FILE STMT [`;']\n\
-       STMT ::= OPTION-STMT | FW-STMT\n\
-       FW-STMT ::= `fw' SOURCE OPTIONS [`to'|`->'] TARGET OPTIONS\n\
-       OPTIONS ::= `{' OPTION-SEQ `}'\n\
-       OPTION-SEQ ::= EMPTY | OPTION-STMT [`;'] OPTION-SEQ\n\
-\n\
-Option syntax\n\
-       OPTION-STMT ::= Q-OPTION\n\
-       Q-OPTION ::= OPTION\n\
-            | PREFIX `.' Q-OPTION\n\
-            | PREFIX `{' OPTION-SEQ `}'\n\
-       PREFIX ::= WORD\n\
-\n\
-File source and target\n\
-       SOURCE ::= FILE\n\
-       TARGET ::= FILE\n\
-       FILE ::= `file' [`.'] FSPEC [`,' FSPEC]\n\
-       FSPEC ::= FD-SPEC | NAME-SPEC | NULL-SPEC\n\
-       FD-SPEC ::= [[`:']`fd'[`:']] NUMBER|`stdin'|`stdout'\n\
-       NAME-SPEC ::= [[`:']`name'[`:']] FILE-NAME\n\
-       FILE-NAME ::= PATH-SEQ | [ PATH-SEQ ]\n\
-       PATH-SEQ ::= PATH-ELT | PATH-SEQ PATH-ELT\n\
-       PATH-ELT ::= `/' | WORD\n\
-       NULL-SPEC ::= [`:']`null'[`:']\n\
-\n\
-Exec source and target\n\
-       SOURCE ::= EXEC\n\
-       TARGET ::= EXEC\n\
-       EXEC ::= `exec' [`.'] CMD-SPEC\n\
-       CMD-SPEC ::= SHELL-CMD | [PROG-NAME] `[' ARGV0 ARG-SEQ `]'\n\
-       ARG-SEQ ::= WORD | ARG-SEQ WORD\n\
-       SHELL-CMD ::= WORD\n\
-       ARGV0 ::= WORD\n\
-\n\
-Socket source and target\n\
-       SOURCE ::= SOCKET-SOURCE\n\
-       TARGET ::= SOCKET-TARGET\n\
-       SOCKET-SOURCE ::= [`socket'[`.']] [[`:']ADDR-TYPE[`:']] SOURCE-ADDR\n\
-       SOCKET-TARGET ::= [`socket'[`.']] [[`:']ADDR-TYPE[`:']] TARGET-ADDR\n\
-\n\
-       INET-SOURCE-ADDR ::= [`port'] PORT\n\
-       INET-TARGET-ADDR ::= ADDRESS [`:'] PORT\n\
-       ADDRESS ::= ADDR-ELT | ADDRESS ADDR-ELT\n\
-       ADDR-ELT ::= `.' | WORD\n\
-\n\
-       UNIX-SOURCE-ADDR ::= FILE-NAME\n\
-       UNIX-TARGET-ADDR ::= FILE-NAME\n\
-");
+  fputs("\nGrammar summary\n\n", fp);
+  fputs(grammar_text, fp);
 }
 
 static void options(FILE *fp)
 {
   version(fp);
-  pquis(fp, "\n\
-Options summary\n\
-\n\
-File attributes (`fattr')\n\
-       prefix.FATTR.MODE [=] MODE\n\
-       prefix.FATTR.OWNER [=] USER\n\
-       prefix.FATTR.GROUP [=] GROUP\n\
-\n\
-File options\n\
-       file.create [=] yes|no\n\
-       file.open [=] no|truncate|append\n\
-       file.fattr.*\n\
-\n\
-Exec options\n\
-       exec.logging [=] yes|no\n\
-       exec.dir [=] FILE-NAME\n\
-       exec.root [=] FILE-NAME\n\
-       exec.user [=] USER\n\
-       exec.group [=] GROUP\n\
-       exec.rlimit.LIMIT[.hard|.soft] [=] VALUE\n\
-       exec.env.clear\n\
-       exec.env.unset VAR\n\
-       exec.env.[set] VAR [=] VALUE\n\
-\n\
-Socket options\n\
-       socket.conn [=] NUMBER|unlimited|one-shot\n\
-       socket.listen [=] NUMBER\n\
-       socket.logging [=] yes|no\n\
-\n\
-       socket.inet.source.[allow|deny] [host] ADDR [/ ADDR]\n\
-       socket.inet.source.[allow|deny] priv-port\n\
-       socket.inet.source.addr [=] any|ADDR\n\
-       socket.inet.dest.addr [=] any|ADDR\n\
-       socket.inet.dest.priv-port [=] yes|no\n\
-\n\
-       socket.unix.fattr.*\n\
-");
+  fputs("\nOption summary\n\n", fp);
+  fputs(option_text, fp);
 }
 
 /* --- @main@ --- *
diff --git a/grammar b/grammar
deleted file mode 100644 (file)
index 02b5b16..0000000
--- a/grammar
+++ /dev/null
@@ -1,81 +0,0 @@
-Basic syntax
-       FILE ::= EMPTY | FILE STMT [`;']
-       STMT ::= OPTION-STMT | FW-STMT
-       FW-STMT ::= `fw' SOURCE OPTIONS [`to'|`->'] TARGET OPTIONS
-       OPTIONS ::= `{' OPTION-SEQ `}'
-       OPTION-SEQ ::= EMPTY | OPTION-STMT [`;'] OPTION-SEQ
-
-Option syntax
-       OPTION-STMT ::= Q-OPTION
-       Q-OPTION ::= OPTION
-            | PREFIX `.' Q-OPTION
-            | PREFIX `{' OPTION-SEQ `}'
-       PREFIX ::= WORD
-
-File source and target
-       SOURCE ::= FILE
-       TARGET ::= FILE
-       FILE ::= `file' [`.'] FSPEC [`,' FSPEC]
-       FSPEC ::= FD-SPEC | NAME-SPEC | NULL-SPEC
-       FD-SPEC ::= [[`:']`fd'[`:']] NUMBER|`stdin'|`stdout'
-       NAME-SPEC ::= [[`:']`file'[`:']] FILE-NAME
-       FILE-NAME ::= PATH-SEQ | [ PATH-SEQ ]
-       PATH-SEQ ::= PATH-ELT | PATH-SEQ PATH-ELT
-       PATH-ELT ::= `/' | WORD
-       NULL-SPEC ::= [`:']`null'[`:']
-
-Exec source and target
-       SOURCE ::= EXEC
-       TARGET ::= EXEC
-       EXEC ::= `exec' [`.'] CMD-SPEC
-       CMD-SPEC ::= SHELL-CMD | [PROG-NAME] `[' ARGV0 ARG-SEQ `]'
-       ARG-SEQ ::= WORD | ARG-SEQ WORD
-       SHELL-CMD ::= WORD
-       ARGV0 ::= WORD
-
-Socket source and target
-       SOURCE ::= SOCKET-SOURCE
-       TARGET ::= SOCKET-TARGET
-       SOCKET-SOURCE ::= [`socket'[`.']] [[`:']ADDR-TYPE[`:']] SOURCE-ADDR
-       SOCKET-TARGET ::= [`socket'[`.']] [[`:']ADDR-TYPE[`:']] TARGET-ADDR
-
-       INET-SOURCE-ADDR ::= [`port'] PORT
-       INET-TARGET-ADDR ::= ADDRESS [`:'] PORT
-       ADDRESS ::= ADDR-ELT | ADDRESS ADDR-ELT
-       ADDR-ELT ::= `.' | WORD
-
-       UNIX-SOURCE-ADDR ::= FILE-NAME
-       UNIX-TARGET-ADDR ::= FILE-NAME
-
-File attributes (`fattr')
-       PREFIX.fattr.mode [=] MODE
-       PREFIX.fattr.owner [=] USER
-       PREFIX.fattr.group [=] GROUP
-
-File options
-       file.create [=] yes|no
-       file.open [=] no|truncate|append
-       file.fattr.*
-
-Exec options
-       exec.logging [=] yes|no
-       exec.dir [=] FILE-NAME
-       exec.root [=] FILE-NAME
-       exec.user [=] USER
-       exec.group [=] GROUP
-       exec.rlimit.LIMIT[.hard|.soft] [=] VALUE
-       exec.env.clear
-       exec.env.unset VAR
-       exec.env.[set] VAR [=] VALUE
-
-Socket options
-       socket.conn [=] NUMBER|unlimited|one-shot
-       socket.logging [=] yes|no
-
-       socket.inet.source.[allow|deny] priv-port
-       socket.inet.source.[allow|deny] [host] ADDR [/ ADDR]
-       socket.inet.source.addr [=] any|ADDR
-       socket.inet.dest.addr [=] any|ADDR
-       oscket.inet.dest.priv-port [=] yes|no
-
-       socket.unix.source.fattr.*
diff --git a/make-manpage b/make-manpage
new file mode 100755 (executable)
index 0000000..b82f4d2
--- /dev/null
@@ -0,0 +1,152 @@
+#! /usr/bin/perl
+
+%ch = ();
+%ord = ();
+$chunk = undef;
+$mode = $ARGV[0] || die "mode?";
+shift;
+
+sub setup {
+  my $r = \%{$ch{$_[0]}};
+  if (exists $r->{$1}) {
+    $r->{$1} .= ".br\n";
+  } else {
+    push @{$ord{$_[0]}}, $1;
+  }
+  $chunk = \$r->{$1};
+}
+while (<>) {
+  if (/^\.GS (.*)$/) {
+    setup('grammar');
+  } elsif (/^\.OS (.*)$/) {
+    setup('option');
+  } elsif (/^\.GE/ || /^\.OD/) {
+    $chunk = undef;
+  } elsif (/^\.GL (.*)$/) {
+    $ch{'grammar'}{$1} .= ".PP\n";
+    next;
+  } elsif (/^\.OL (.*)$/) {
+    $ch{'option'}{$1} .= ".PP\n";
+    next;
+  } elsif (/^@@@ (\w+)$/) {
+    if ($mode eq 'man') {
+      foreach $head (@{$ord{$1}}) {
+       print ".SS $head\n";
+       print $ch{$1}{$head};
+      }
+    }
+    next;
+  } else {
+    $$chunk .= $_ if $chunk;
+  }
+  print if $mode eq 'man';
+}
+
+sub ital { uc($_[0]); }
+sub bold { $head eq 'option' ? $_[0] : "`$_[0]'"; }
+sub rom { $_[0]; }
+
+sub deroff {
+  $head = $_[0];
+  my $chsep = "";
+  my $out = "";
+  for my $chunk (@{$ord{$head}}) {
+    (my $chh = $chunk) =~ s/^"(.*)"$/$1/;
+    $out .= "$chsep$chh\n";
+    $chsep = "\n";
+    $sep = "\t";
+    for (split /\n/, $ch{$head}{$chunk}) {
+      s/\s$//; s/\\\&//g; s/\\-/-/g;
+      if (/^\.I (.*)$/) {
+       $out .= $sep . ital($1);
+       $sep = " ";
+      } elsif (/^\.B (.*)$/) {
+       $out .= $sep . bold($1);
+       $sep = " ";
+      } elsif (/^\.RB (.*)/) {
+       my $i = 1; $out .= $sep; $sep = " ";
+       for my $w (split ' ', $1) {
+         if ($w eq "\\c") {
+           $sep = ""; last;
+         }
+         $out .= ($i++%2 ? rom($w) : bold($w));
+       }
+      } elsif (/^\.BR (.*)/) {
+       my $i = 1; $out .= $sep; $sep = " ";
+       for my $w (split ' ', $1) {
+         if ($w eq "\\c") {
+           $sep = ""; last;
+         }
+         $out .= ($i++%2 ? bold($w) : rom($w));
+       }
+      } elsif (/^\.IR (.*)/) {
+       my $i = 1; $out .= $sep; $sep = " ";
+       for my $w (split ' ', $1) {
+         if ($w eq "\\c") {
+           $sep = ""; last;
+         }
+         $out .= ($i++%2 ? ital($w) : rom($w));
+       }
+      } elsif (/^\.RI (.*)/) {
+       my $i = 1; $out .= $sep; $sep = " ";
+       for my $w (split ' ', $1) {
+         if ($w eq "\\c") {
+           $sep = ""; last;
+         }
+         $out .= ($i++%2 ? rom($w) : ital($w));
+       }
+      } elsif (/^\.BI (.*)/) {
+       my $i = 1; $out .= $sep; $sep = " ";
+       for my $w (split ' ', $1) {
+         if ($w eq "\\c") {
+           $sep = ""; last;
+         }
+         $out .= ($i++%2 ? bold($w) : ital($w));
+       }
+      } elsif (/^\.IB (.*)/) {
+       my $i = 1; $out .= $sep; $sep = " ";
+       for my $w (split ' ', $1) {
+         if ($w eq "\\c") {
+           $sep = ""; last;
+         }
+         $out .= ($i++%2 ? ital($w) : bold($w));
+       }
+      } elsif (/^\.$/) {
+       # foo
+      } elsif (/^[^.]/) {
+       $out .= $sep . $_;
+       $sep = " "; 
+      } elsif (/^\.br$/ || /^\.PP$/) {
+       $out .= "\n";
+       $sep = "\t";
+      }
+    }
+    $out .= "\n";
+  }
+  return $out;
+}
+
+if ($mode eq 'c') {
+  print <<EOF;
+/* -*-c-*-
+ *
+ * Generated grammar and options summary
+ */
+
+#include "mantext.h"
+
+EOF
+  for $head (keys %ch) {
+    print "const char ${head}_text[] = \"\\\n";
+    $text = deroff($head);
+    $text =~ s/\n/\\n\\\n/g;
+    print $text;
+    print "\";\n\n";
+  }
+} elsif ($mode eq 'text') {
+  my $sep = "";
+  for $head (keys %ch) {
+    print $sep, deroff($head);
+    $sep = "\n";
+  }
+}
diff --git a/mantext.h b/mantext.h
new file mode 100644 (file)
index 0000000..0d83dd9
--- /dev/null
+++ b/mantext.h
@@ -0,0 +1,47 @@
+/* -*-c-*-
+ *
+ * $Id$
+ *
+ * Grammar declarations
+ *
+ * (c) 2006 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of the `fw' port forwarder.
+ *
+ * `fw' is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * `fw' is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with `fw'; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef MANTEXT_H
+#define MANTEXT_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Data provided -----------------------------------------------------*/
+
+extern const char grammar_text[];
+extern const char option_text[];
+    
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif