From c47feff83132b1850c53acf7bfe182f5ce94adc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dag-Erling=20Sm=C3=B8rgrav?= Date: Mon, 18 Feb 2002 19:08:28 +0000 Subject: [PATCH] Teach gendoc to parse a simple syntax for inline documentation. Sponsored by: DARPA, NAI Labs git-svn-id: svn+ssh://svn.openpam.org/svn/openpam/trunk@62 185d5e19-27fe-0310-9dcf-9bff6b9f3609 --- misc/gendoc.pl | 330 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 287 insertions(+), 43 deletions(-) diff --git a/misc/gendoc.pl b/misc/gendoc.pl index d336fbe..48eea36 100644 --- a/misc/gendoc.pl +++ b/misc/gendoc.pl @@ -38,37 +38,74 @@ use strict; use Fcntl; use POSIX qw(strftime); -use vars qw($COPYRIGHT $TODAY %FUNCTIONS); +use vars qw($COPYRIGHT $TODAY %FUNCTIONS %PAMERR); -sub gendoc($) { +%PAMERR = ( + PAM_SUCCESS => "Success", + PAM_OPEN_ERR => "Failed to load module", + PAM_SYMBOL_ERR => "Invalid symbol", + PAM_SERVICE_ERR => "Error in service module", + PAM_SYSTEM_ERR => "System error", + PAM_BUF_ERR => "Memory buffer error", + PAM_CONV_ERR => "Conversation failure", + PAM_PERM_DENIED => "Permission denied", + PAM_MAXTRIES => "Maximum number of tries exceeded", + PAM_AUTH_ERR => "Authentication error", + PAM_NEW_AUTHTOK_REQD => "New authentication token required", + PAM_CRED_INSUFFICIENT => "Insufficient credentials", + PAM_AUTHINFO_UNAVAIL => "Authentication information is unavailable", + PAM_USER_UNKNOWN => "Unknown user", + PAM_CRED_UNAVAIL => "Failed to retrieve user credentials", + PAM_CRED_EXPIRED => "User credentials have expired", + PAM_CRED_ERR => "Failed to set user credentials", + PAM_ACCT_EXPIRED => "User accound has expired", + PAM_AUTHTOK_EXPIRED => "Password has expired", + PAM_SESSION_ERR => "Session failure", + PAM_AUTHTOK_ERR => "Authentication token failure", + PAM_AUTHTOK_RECOVERY_ERR => "Failed to recover old authentication token", + PAM_AUTHTOK_LOCK_BUSY => "Authentication token lock busy", + PAM_AUTHTOK_DISABLE_AGING => "Authentication token aging disabled", + PAM_NO_MODULE_DATA => "Module data not found", + PAM_IGNORE => "Ignore this module", + PAM_ABORT => "General failure", + PAM_TRY_AGAIN => "Try again", + PAM_MODULE_UNKNOWN => "Unknown module type", + PAM_DOMAIN_UNKNOWN => "Unknown authentication domain", +); + +sub parse_source($) { my $fn = shift; local *FILE; my $source; - my $mdoc; my $func; my $descr; my $type; my $args; - my $name; + my $argnames; + my $man; + my $inlist; + my $inliteral; + my %xref; + my @errors; if ($fn !~ m,\.c$,) { warn("$fn: not C source, ignoring\n"); return; } - + sysopen(FILE, $fn, O_RDONLY) or die("$fn: open(): $!\n"); $source = join('', ); close(FILE); - if ($source =~ m,^(/\*-\n.*?)\s*\*/,s) { - $mdoc = $1; - $mdoc =~ s,^.\*,.\\\",gm; - $mdoc .= "\n.\\\"\n"; - $COPYRIGHT = $mdoc; - } else { - $mdoc = ".\\\" \$" . "Id" . "\$\n"; + return if ($source =~ m/^ \* NOPARSE\s*$/m); + + if (!defined($COPYRIGHT) && $source =~ m,^(/\*-\n.*?)\s*\*/,s) { + $COPYRIGHT = $1; + $COPYRIGHT =~ s,^.\*,.\\\",gm; + $COPYRIGHT =~ s,(\$Id).*?\$,$1\$,; + $COPYRIGHT .= "\n.\\\""; } $func = $fn; $func =~ s,^(?:.*/)?([^/]+)\.c$,$1,; @@ -85,34 +122,224 @@ sub gendoc($) { $args =~ s/,\s+/, /gs; $args = "\"$args\""; - $FUNCTIONS{$func} = [ $type, $args ]; - - $mdoc .= ".Dd $TODAY -.Dt " . uc($func) . " 3 + %xref = ( + "pam 3" => 1 + ); + + if ($type eq "int") { + foreach (split("\n", $source)) { + next unless (m/^ \*\s+(!?PAM_[A-Z_]+|=[a-z_]+)\s*$/); + push(@errors, $1); + } + $xref{"pam_strerror 3"} = 1; + } + + $argnames = $args; + $argnames =~ s/\"[^\"]+\*?\b(\w+)\"/\"$1\"/g; + $argnames =~ s/([\|\[\]\(\)\.\*\+\?])/\\$1/g; + $argnames =~ s/\" \"/|/g; + $argnames =~ s/^\"(.*)\"$/($1)/; + foreach (split("\n", $source)) { + s/\s*$//; + if (!defined($man)) { + if (m/^\/\*\*$/) { + $man = ""; + } + next; + } + last if (m/^ \*\/$/); + s/^ \* ?//; + s/\\(.)/$1/gs; + if (m/^$/) { + if ($man ne "" && $man !~ m/\.Pp\n$/s) { + if ($inliteral) { + $man .= "\0\n"; + } elsif ($inlist) { + $man .= ".El\n"; + $inlist = 0; + } else { + $man .= ".Pp\n"; + } + } + next; + } + if (m/^>(\w+)(?:\s+(\d))?$/) { + ++$xref{$2 ? "$1 $2" : "$1 3"}; + next; + } + if (s/^\s+(=?\w+):\s*/.It $1/) { + if ($inliteral) { + $man .= ".Ed\n"; + $inliteral = 0; + } + if (!$inlist) { + $man =~ s/\.Pp\n$//s; + $man .= ".Bl -tag -width 18n\n"; + $inlist = 1; + } + s/^\.It =([A-Z][A-Z_]+)$/.It Dv $1/gs; + $man .= "$_\n"; + next; + } elsif ($inlist && m/^\S/) { + $man .= ".El\n"; + $inlist = 0; + } elsif ($inliteral && m/^\S/) { + $man .= ".Ed\n"; + $inlist = 0; + } elsif ($inliteral) { + $man .= "$_\n"; + next; + } elsif ($inlist) { + s/^\s+//; + } elsif (m/^\s+/) { + $man .= ".Bd -literal\n"; + $inliteral = 1; + $man .= "$_\n"; + next; + } + s/\s*=$func\b\s*/\n.Nm\n/gs; + s/\s*=$argnames\b\s*/\n.Va $1\n/gs; + s/\s*=(struct \w+(?: \*)?)\b\s*/\n.Vt $1\n/gs; + if (s/\s*=([a-z_]+)\b\s*/\n.Xr $1 3\n/gs) { + ++$xref{"$1 3"}; + } + s/\s*\"(?=\w)/\n.Do\n/gs; + s/\"(?!\w)\s*/\n.Dc\n/gs; + s/\s*=([A-Z][A-Z_]+)\b\s*(?![\.,:;])/\n.Dv $1\n/gs; + s/\s*=([A-Z][A-Z_]+)\b([\.,:;]+)\s*/\n.Dv $1 $2\n/gs; + s/\s*{([A-Z][a-z] .*?)}\s*/\n.$1\n/gs; + $man .= "$_\n"; + } + if (defined($man)) { + $man =~ s/(\n\.[A-Z][a-z] [\w ]+)\n([\.,:;-]\S*)\s*/$1 $2\n/gs; + $man =~ s/\s*$/\n/gm; + $man =~ s/\n+/\n/gs; + $man =~ s/\0//gs; + chomp($man); + } else { + $man = "No description available."; + } + + $FUNCTIONS{$func} = { + 'name' => $func, + 'descr' => $descr, + 'type' => $type, + 'args' => $args, + 'man' => $man, + 'xref' => \%xref, + 'errors' => \@errors, + }; + if ($source =~ m/^ \* NODOC\s*$/m) { + $FUNCTIONS{$func}->{'nodoc'} = 1; + $FUNCTIONS{$func}->{'nolist'} = 1; + } + if ($source =~ m/^ \* NOLIST\s*$/m) { + $FUNCTIONS{$func}->{'nolist'} = 1; + } +} + +sub expand_errors($); +sub expand_errors($) { + my $func = shift; # Ref to function hash + + my %errors; + + if (defined($func->{'recursed'})) { + warn("$func->{'name'}(): loop in error spec\n"); + return qw(); + } + $func->{'recursed'} = 1; + + foreach (@{$func->{'errors'}}) { + if (m/^(PAM_[A-Z_]+)$/) { + if (!defined($PAMERR{$1})) { + warn("$func->{'name'}(): unrecognized error: $1\n"); + next; + } + $errors{$1} = 1; + } elsif (m/^!(PAM_[A-Z_]+)$/) { + # treat negations separately + } elsif (m/^=([a-z_]+)$/) { + if (!defined($FUNCTIONS{$1})) { + warn("$func->{'name'}(): reference to unknown $1()\n"); + next; + } + foreach (expand_errors($FUNCTIONS{$1})) { + $errors{$_} = 1; + } + } else { + warn("$func->{'name'}(): invalid error specification: $_\n"); + } + } + foreach (@{$func->{'errors'}}) { + if (m/^!(PAM_[A-Z_]+)$/) { + delete($errors{$1}); + } + } + delete($func->{'recursed'}); + return (sort(keys(%errors))); +} + +sub gendoc($) { + my $func = shift; # Ref to function hash + + local *FILE; + my $mdoc; + my $fn; + + return if defined($func->{'nodoc'}); + + $mdoc = "$COPYRIGHT +.Dd $TODAY +.Dt " . uc($func->{'name'}) . " 3 .Os .Sh NAME -.Nm $func -.Nd $descr +.Nm $func->{'name'} +.Nd $func->{'descr'} .Sh LIBRARY .Lb libpam .Sh SYNOPSIS .In security/pam_appl.h -.Ft $type -.Fn $func $args +"; + if ($func->{'name'} =~ m/_sm_/) { + $mdoc .= ".In security/pam_modules.h\n" + } + $mdoc .= ".Ft $func->{'type'} +.Fn $func->{'name'} $func->{'args'} .Sh DESCRIPTION +$func->{'man'} +"; + if ($func->{'type'} eq "int") { + $mdoc .= ".Sh RETURN VALUES The .Nm -function is not yet documented. -.Sh RETURN VALUES -The -.Fn function returns one of the following values: -.Bl -tag -width PAM_AUTHTOK_DISABLE_AGING -.El -.Sh SEE ALSO -.Xr pam_strerror 3 , -.Xr pam 3 -.Sh STANDARDS +.Bl -tag -width 18n +"; + my @errors = expand_errors($func); + warn("$func->{'name'}(): no error specification\n") + unless(@errors); + foreach (@errors) { + $mdoc .= ".It Bq Er $_\n$PAMERR{$_}.\n"; + } + $mdoc .= ".El\n"; + } else { + if ($func->{'type'} =~ m/\*$/) { + $mdoc .= ".Sh RETURN VALUES +The +.Nm +function returns +.Dv NULL +on failure. +"; + } + } + $mdoc .= ".Sh SEE ALSO\n"; + my @xref = sort(keys(%{$func->{'xref'}})); + while (@xref) { + $mdoc .= ".Xr " . shift(@xref) . (@xref ? " ,\n" : "\n"); + } + $mdoc .= ".Sh STANDARDS .Rs .%T \"X/Open Single Sign-On Service (XSSO) - Pluggable Authentication Modules\" .%D \"June 1997\" @@ -127,24 +354,27 @@ Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 as part of the DARPA CHATS research program. "; - $fn = "$func.3"; - sysopen(FILE, $fn, O_RDWR|O_CREAT|O_TRUNC) + $fn = "$func->{'name'}.3"; + sysopen(FILE, $fn, O_RDWR|O_CREAT|O_TRUNC) or die("$fn: open(): $!\n"); - print(FILE $mdoc); - close(FILE); + print(FILE $mdoc); + close(FILE); } sub gensummary() { - print "$COPYRIGHT"; - print ".Dd $TODAY + my $func; + + print "$COPYRIGHT +.Dd $TODAY .Dt PAM 3 .Os .Sh NAME "; my @funcs = sort(keys(%FUNCTIONS)); - while (@funcs) { - print ".Nm " . shift(@funcs) . (@funcs ? " ,\n" : "\n"); + while ($func = shift(@funcs)) { + next if (defined($FUNCTIONS{$func}->{'nolist'})); + print ".Nm $func". (@funcs ? " ,\n" : "\n"); } print ".Nd Pluggable Authentication Modules Library .Sh LIBRARY @@ -152,15 +382,26 @@ sub gensummary() { .Sh SYNOPSIS .In security/pam_appl.h "; - foreach my $func (sort(keys(%FUNCTIONS))) { - print ".Ft $FUNCTIONS{$func}->[0]\n"; - print ".Fn $func $FUNCTIONS{$func}->[1]\n"; + foreach $func (sort(keys(%FUNCTIONS))) { + next if (defined($FUNCTIONS{$func}->{'nolist'})); + print ".Ft $FUNCTIONS{$func}->{'type'}\n"; + print ".Fn $func $FUNCTIONS{$func}->{'args'}\n"; } print ".Sh DESCRIPTION .Sh RETURN VALUES +The following return codes are defined in the +.In security/pam_constants.h +header: +.Bl -tag -width 18n +"; + foreach (sort(keys(%PAMERR))) { + print ".It Bq Er $_\n$PAMERR{$_}.\n"; + } + print ".El .Sh SEE ALSO "; - foreach my $func (sort(keys(%FUNCTIONS))) { + foreach $func (sort(keys(%FUNCTIONS))) { + next if (defined($FUNCTIONS{$func}->{'nolist'})); print ".Xr $func 3 ,\n"; } print ".Xr pam.conf 5 @@ -183,7 +424,10 @@ MAIN:{ $TODAY = strftime("%B %e, %Y", localtime(time())); $TODAY =~ s,\s+, ,g; foreach my $fn (@ARGV) { - gendoc($fn); + parse_source($fn); + } + foreach my $func (values(%FUNCTIONS)) { + gendoc($func); } gensummary(); }