From: Mark Wooding Date: Wed, 22 Feb 2006 02:07:00 +0000 (+0000) Subject: docs: Generate grammar and option summaries from manpage. X-Git-Tag: 1.3.0~8 X-Git-Url: https://git.distorted.org.uk/~mdw/fwd/commitdiff_plain/8cf7c7c277cbdea379003dc8e872392b8c519c7d docs: Generate grammar and option summaries from manpage. 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... --- diff --git a/.gitignore b/.gitignore index 51c0f7e..5108762 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ mkinstalldirs depcomp autom4te.cache +GRAMMAR diff --git a/Makefile.am b/Makefile.am index 40dd33d..2df8226 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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 --------------------------------------------------- diff --git a/debian/rules b/debian/rules index 3970631..21b6cee 100755 --- a/debian/rules +++ b/debian/rules @@ -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 --- 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 --- 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 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 index 0000000..b82f4d2 --- /dev/null +++ b/make-manpage @@ -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 <