Home | History | Annotate | Line # | Download | only in misc
gendoc.pl revision 1.1
      1 #!/usr/bin/perl -w
      2 #-
      3 # Copyright (c) 2002-2003 Networks Associates Technology, Inc.
      4 # Copyright (c) 2004-2011 Dag-Erling Smrgrav
      5 # All rights reserved.
      6 #
      7 # This software was developed for the FreeBSD Project by ThinkSec AS and
      8 # Network Associates Laboratories, the Security Research Division of
      9 # Network Associates, Inc.  under DARPA/SPAWAR contract N66001-01-C-8035
     10 # ("CBOSS"), as part of the DARPA CHATS research program.
     11 #
     12 # Redistribution and use in source and binary forms, with or without
     13 # modification, are permitted provided that the following conditions
     14 # are met:
     15 # 1. Redistributions of source code must retain the above copyright
     16 #    notice, this list of conditions and the following disclaimer.
     17 # 2. Redistributions in binary form must reproduce the above copyright
     18 #    notice, this list of conditions and the following disclaimer in the
     19 #    documentation and/or other materials provided with the distribution.
     20 # 3. The name of the author may not be used to endorse or promote
     21 #    products derived from this software without specific prior written
     22 #    permission.
     23 #
     24 # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
     25 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     26 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     27 # ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     28 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     29 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     30 # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     31 # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     32 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     33 # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     34 # SUCH DAMAGE.
     35 #
     36 # Id: gendoc.pl 465 2011-11-02 20:34:26Z des
     37 #
     38 
     39 use strict;
     40 use locale;
     41 use Fcntl;
     42 use Getopt::Std;
     43 use POSIX qw(locale_h strftime);
     44 use vars qw($COPYRIGHT %AUTHORS $TODAY %FUNCTIONS %PAMERR);
     45 
     46 $COPYRIGHT = ".\\\"-
     47 .\\\" Copyright (c) 2001-2003 Networks Associates Technology, Inc.
     48 .\\\" Copyright (c) 2004-2011 Dag-Erling Smrgrav
     49 .\\\" All rights reserved.
     50 .\\\"
     51 .\\\" This software was developed for the FreeBSD Project by ThinkSec AS and
     52 .\\\" Network Associates Laboratories, the Security Research Division of
     53 .\\\" Network Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035
     54 .\\\" (\"CBOSS\"), as part of the DARPA CHATS research program.
     55 .\\\"
     56 .\\\" Redistribution and use in source and binary forms, with or without
     57 .\\\" modification, are permitted provided that the following conditions
     58 .\\\" are met:
     59 .\\\" 1. Redistributions of source code must retain the above copyright
     60 .\\\"    notice, this list of conditions and the following disclaimer.
     61 .\\\" 2. Redistributions in binary form must reproduce the above copyright
     62 .\\\"    notice, this list of conditions and the following disclaimer in the
     63 .\\\"    documentation and/or other materials provided with the distribution.
     64 .\\\" 3. The name of the author may not be used to endorse or promote
     65 .\\\"    products derived from this software without specific prior written
     66 .\\\"    permission.
     67 .\\\"
     68 .\\\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
     69 .\\\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     70 .\\\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     71 .\\\" ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     72 .\\\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     73 .\\\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     74 .\\\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     75 .\\\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     76 .\\\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     77 .\\\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     78 .\\\" SUCH DAMAGE.
     79 .\\\"
     80 .\\\" \$" . "Id" . "\$
     81 .\\\"";
     82 
     83 %AUTHORS = (
     84     THINKSEC => "ThinkSec AS and Network Associates Laboratories, the
     85 Security Research Division of Network Associates, Inc.\\& under
     86 DARPA/SPAWAR contract N66001-01-C-8035
     87 .Pq Dq CBOSS ,
     88 as part of the DARPA CHATS research program.",
     89     DES => ".An Dag-Erling Sm\\(/orgrav Aq des\@FreeBSD.org .",
     90 );
     91 
     92 %PAMERR = (
     93     PAM_SUCCESS			=> "Success",
     94     PAM_OPEN_ERR		=> "Failed to load module",
     95     PAM_SYMBOL_ERR		=> "Invalid symbol",
     96     PAM_SERVICE_ERR		=> "Error in service module",
     97     PAM_SYSTEM_ERR		=> "System error",
     98     PAM_BUF_ERR			=> "Memory buffer error",
     99     PAM_CONV_ERR		=> "Conversation failure",
    100     PAM_PERM_DENIED		=> "Permission denied",
    101     PAM_MAXTRIES		=> "Maximum number of tries exceeded",
    102     PAM_AUTH_ERR		=> "Authentication error",
    103     PAM_NEW_AUTHTOK_REQD	=> "New authentication token required",
    104     PAM_CRED_INSUFFICIENT	=> "Insufficient credentials",
    105     PAM_AUTHINFO_UNAVAIL	=> "Authentication information is unavailable",
    106     PAM_USER_UNKNOWN		=> "Unknown user",
    107     PAM_CRED_UNAVAIL		=> "Failed to retrieve user credentials",
    108     PAM_CRED_EXPIRED		=> "User credentials have expired",
    109     PAM_CRED_ERR		=> "Failed to set user credentials",
    110     PAM_ACCT_EXPIRED		=> "User account has expired",
    111     PAM_AUTHTOK_EXPIRED		=> "Password has expired",
    112     PAM_SESSION_ERR		=> "Session failure",
    113     PAM_AUTHTOK_ERR		=> "Authentication token failure",
    114     PAM_AUTHTOK_RECOVERY_ERR	=> "Failed to recover old authentication token",
    115     PAM_AUTHTOK_LOCK_BUSY	=> "Authentication token lock busy",
    116     PAM_AUTHTOK_DISABLE_AGING	=> "Authentication token aging disabled",
    117     PAM_NO_MODULE_DATA		=> "Module data not found",
    118     PAM_IGNORE			=> "Ignore this module",
    119     PAM_ABORT			=> "General failure",
    120     PAM_TRY_AGAIN		=> "Try again",
    121     PAM_MODULE_UNKNOWN		=> "Unknown module type",
    122     PAM_DOMAIN_UNKNOWN		=> "Unknown authentication domain",
    123 );
    124 
    125 sub parse_source($) {
    126     my $fn = shift;
    127 
    128     local *FILE;
    129     my $source;
    130     my $func;
    131     my $descr;
    132     my $type;
    133     my $args;
    134     my $argnames;
    135     my $man;
    136     my $inlist;
    137     my $intaglist;
    138     my $inliteral;
    139     my %xref;
    140     my @errors;
    141     my $author;
    142 
    143     if ($fn !~ m,\.c$,) {
    144 	warn("$fn: not C source, ignoring\n");
    145 	return undef;
    146     }
    147 
    148     open(FILE, "<", "$fn")
    149 	or die("$fn: open(): $!\n");
    150     $source = join('', <FILE>);
    151     close(FILE);
    152 
    153     return undef
    154 	if ($source =~ m/^ \* NOPARSE\s*$/m);
    155 
    156     $author = 'THINKSEC';
    157     if ($source =~ s/^ \* AUTHOR\s+(.*?)\s*$//m) {
    158 	$author = $1;
    159     }
    160 
    161     $func = $fn;
    162     $func =~ s,^(?:.*/)?([^/]+)\.c$,$1,;
    163     if ($source !~ m,\n \* ([\S ]+)\n \*/\n\n([\S ]+)\n$func\((.*?)\)\n\{,s) {
    164 	warn("$fn: can't find $func\n");
    165 	return undef;
    166     }
    167     ($descr, $type, $args) = ($1, $2, $3);
    168     $descr =~ s,^([A-Z][a-z]),lc($1),e;
    169     $descr =~ s,[\.\s]*$,,;
    170     while ($args =~ s/^((?:[^\(]|\([^\)]*\))*),\s*/$1\" \"/g) {
    171 	# nothing
    172     }
    173     $args =~ s/,\s+/, /gs;
    174     $args = "\"$args\"";
    175 
    176     %xref = (
    177 	3 => { 'pam' => 1 },
    178     );
    179 
    180     if ($type eq "int") {
    181 	foreach (split("\n", $source)) {
    182 	    next unless (m/^ \*\s+(!?PAM_[A-Z_]+|=[a-z_]+)\s*$/);
    183 	    push(@errors, $1);
    184 	}
    185 	++$xref{3}->{'pam_strerror'};
    186     }
    187 
    188     $argnames = $args;
    189     # extract names of regular arguments
    190     $argnames =~ s/\"[^\"]+\*?\b(\w+)\"/\"$1\"/g;
    191     # extract names of function pointer arguments
    192     $argnames =~ s/\"([\w\s\*]+)\(\*?(\w+)\)\([^\)]+\)\"/\"$2\"/g;
    193     # escape metacharacters (there shouldn't be any, but...)
    194     $argnames =~ s/([\|\[\]\(\)\.\*\+\?])/\\$1/g;
    195     # separate argument names with |
    196     $argnames =~ s/\" \"/|/g;
    197     # and surround with ()
    198     $argnames =~ s/^\"(.*)\"$/($1)/;
    199     # $argnames is now a regexp that matches argument names
    200     $inliteral = $inlist = $intaglist = 0;
    201     foreach (split("\n", $source)) {
    202 	s/\s*$//;
    203 	if (!defined($man)) {
    204 	    if (m/^\/\*\*$/) {
    205 		$man = "";
    206 	    }
    207 	    next;
    208 	}
    209 	last if (m/^ \*\/$/);
    210 	s/^ \* ?//;
    211 	s/\\(.)/$1/gs;
    212 	if (m/^$/) {
    213 	    # paragraph separator
    214 	    if ($man ne "" && $man !~ m/\.Pp\n$/s) {
    215 		if ($inliteral) {
    216 		    $man .= "\0\n";
    217 		} elsif ($inlist || $intaglist) {
    218 		    $man .= ".El\n.Pp\n";
    219 		    $inlist = $intaglist = 0;
    220 		} else {
    221 		    $man .= ".Pp\n";
    222 		}
    223 	    }
    224 	    next;
    225 	}
    226 	if (m/^>(\w+)(\s+\d)?$/) {
    227 	    # "see also" cross-reference
    228 	    my ($page, $sect) = ($1, $2 ? int($2) : 3);
    229 	    ++$xref{$sect}->{$page};
    230 	    next;
    231 	}
    232 	if (s/^\s+-\s+//) {
    233 	    # item in bullet list
    234 	    if ($inliteral) {
    235 		$man .= ".Ed\n";
    236 		$inliteral = 0;
    237 	    }
    238 	    if ($intaglist) {
    239 		$man .= ".El\n.Pp\n";
    240 		$intaglist = 0;
    241 	    }
    242 	    if (!$inlist) {
    243 		$man =~ s/\.Pp\n$//s;
    244 		$man .= ".Bl -bullet\n";
    245 		$inlist = 1;
    246 	    }
    247 	    $man .= ".It\n";
    248 	    # fall through
    249 	} elsif (s/^\s+(\S+):\s*/.It $1/) {
    250 	    # item in tag list
    251 	    if ($inliteral) {
    252 		$man .= ".Ed\n";
    253 		$inliteral = 0;
    254 	    }
    255 	    if ($inlist) {
    256 		$man .= ".El\n.Pp\n";
    257 		$inlist = 0;
    258 	    }
    259 	    if (!$intaglist) {
    260 		$man =~ s/\.Pp\n$//s;
    261 		$man .= ".Bl -tag -width 18n\n";
    262 		$intaglist = 1;
    263 	    }
    264 	    s/^\.It =([A-Z][A-Z_]+)$/.It Dv $1/gs;
    265 	    $man .= "$_\n";
    266 	    next;
    267 	} elsif (($inlist || $intaglist) && m/^\S/) {
    268 	    # regular text after list
    269 	    $man .= ".El\n.Pp\n";
    270 	    $inlist = $intaglist = 0;
    271 	} elsif ($inliteral && m/^\S/) {
    272 	    # regular text after literal section
    273 	    $man .= ".Ed\n";
    274 	    $inliteral = 0;
    275 	} elsif ($inliteral) {
    276 	    # additional text within literal section
    277 	    $man .= "$_\n";
    278 	    next;
    279 	} elsif ($inlist || $intaglist) {
    280 	    # additional text within list
    281 	    s/^\s+//;
    282 	} elsif (m/^\s+/) {
    283 	    # new literal section
    284 	    $man .= ".Bd -literal\n";
    285 	    $inliteral = 1;
    286 	    $man .= "$_\n";
    287 	    next;
    288 	}
    289 	s/\s*=$func\b\s*/\n.Nm\n/gs;
    290 	s/\s*=$argnames\b\s*/\n.Fa $1\n/gs;
    291 	s/\s*=(struct \w+(?: \*)?)\b\s*/\n.Vt $1\n/gs;
    292 	s/\s*:([a-z_]+)\b\s*/\n.Va $1\n/gs;
    293 	s/\s*;([a-z_]+)\b\s*/\n.Dv $1\n/gs;
    294 	while (s/\s*=([a-z_]+)\b\s*/\n.Xr $1 3\n/s) {
    295 	    ++$xref{3}->{$1};
    296 	}
    297 	s/\s*\"(?=\w)/\n.Do\n/gs;
    298 	s/\"(?!\w)\s*/\n.Dc\n/gs;
    299 	s/\s*=([A-Z][A-Z_]+)\b\s*(?![\.,:;])/\n.Dv $1\n/gs;
    300 	s/\s*=([A-Z][A-Z_]+)\b([\.,:;]+)\s*/\n.Dv $1 $2\n/gs;
    301 	s/\s*{([A-Z][a-z] .*?)}\s*/\n.$1\n/gs;
    302 	$man .= "$_\n";
    303     }
    304     if (defined($man)) {
    305 	if ($inlist || $intaglist) {
    306 	    $man .= ".El\n";
    307 	    $inlist = $intaglist = 0;
    308 	}
    309 	if ($inliteral) {
    310 	    $man .= ".Ed\n";
    311 	    $inliteral = 0;
    312 	}
    313 	$man =~ s/\%/\\&\%/gs;
    314 	$man =~ s/(\n\.[A-Z][a-z] [\w ]+)\n([\.,:;-]\S*)\s*/$1 $2\n/gs;
    315 	$man =~ s/\s*$/\n/gm;
    316 	$man =~ s/\n+/\n/gs;
    317 	$man =~ s/\0//gs;
    318 	$man =~ s/\n\n\./\n\./gs;
    319 	chomp($man);
    320     } else {
    321 	$man = "No description available.";
    322     }
    323 
    324     $FUNCTIONS{$func} = {
    325 	'source'	=> $fn,
    326 	'name'		=> $func,
    327 	'descr'		=> $descr,
    328 	'type'		=> $type,
    329 	'args'		=> $args,
    330 	'man'		=> $man,
    331 	'xref'		=> \%xref,
    332 	'errors'	=> \@errors,
    333 	'author'	=> $author,
    334     };
    335     if ($source =~ m/^ \* NODOC\s*$/m) {
    336 	$FUNCTIONS{$func}->{'nodoc'} = 1;
    337     }
    338     if ($source !~ m/^ \* XSSO \d/m) {
    339 	$FUNCTIONS{$func}->{'openpam'} = 1;
    340     }
    341     expand_errors($FUNCTIONS{$func});
    342     return $FUNCTIONS{$func};
    343 }
    344 
    345 sub expand_errors($);
    346 sub expand_errors($) {
    347     my $func = shift;		# Ref to function hash
    348 
    349     my %errors;
    350     my $ref;
    351     my $fn;
    352 
    353     if (defined($func->{'recursed'})) {
    354 	warn("$func->{'name'}(): loop in error spec\n");
    355 	return qw();
    356     }
    357     $func->{'recursed'} = 1;
    358 
    359     foreach (@{$func->{'errors'}}) {
    360 	if (m/^(PAM_[A-Z_]+)$/) {
    361 	    if (!defined($PAMERR{$1})) {
    362 		warn("$func->{'name'}(): unrecognized error: $1\n");
    363 		next;
    364 	    }
    365 	    $errors{$1} = 1;
    366 	} elsif (m/^!(PAM_[A-Z_]+)$/) {
    367 	    # treat negations separately
    368 	} elsif (m/^=([a-z_]+)$/) {
    369 	    $ref = $1;
    370 	    if (!defined($FUNCTIONS{$ref})) {
    371 		$fn = $func->{'source'};
    372 		$fn =~ s/$func->{'name'}/$ref/;
    373 		parse_source($fn);
    374 	    }
    375 	    if (!defined($FUNCTIONS{$ref})) {
    376 		warn("$func->{'name'}(): reference to unknown $ref()\n");
    377 		next;
    378 	    }
    379 	    foreach (@{$FUNCTIONS{$ref}->{'errors'}}) {
    380 		$errors{$_} = 1;
    381 	    }
    382 	} else {
    383 	    warn("$func->{'name'}(): invalid error specification: $_\n");
    384 	}
    385     }
    386     foreach (@{$func->{'errors'}}) {
    387 	if (m/^!(PAM_[A-Z_]+)$/) {
    388 	    delete($errors{$1});
    389 	}
    390     }
    391     delete($func->{'recursed'});
    392     $func->{'errors'} = [ sort(keys(%errors)) ];
    393 }
    394 
    395 sub dictionary_order($$) {
    396     my ($a, $b) = @_;
    397 
    398     $a =~ s/[^[:alpha:]]//g;
    399     $b =~ s/[^[:alpha:]]//g;
    400     $a cmp $b;
    401 }
    402 
    403 sub genxref($) {
    404     my $xref = shift;		# References
    405 
    406     my $mdoc = '';
    407     my @refs = ();
    408     foreach my $sect (sort(keys(%{$xref}))) {
    409 	foreach my $page (sort(dictionary_order keys(%{$xref->{$sect}}))) {
    410 	    push(@refs, "$page $sect");
    411 	}
    412     }
    413     while ($_ = shift(@refs)) {
    414 	$mdoc .= ".Xr $_" .
    415 	    (@refs ? " ,\n" : "\n");
    416     }
    417     return $mdoc;
    418 }
    419 
    420 sub gendoc($) {
    421     my $func = shift;		# Ref to function hash
    422 
    423     local *FILE;
    424     my $mdoc;
    425     my $fn;
    426 
    427     return if defined($func->{'nodoc'});
    428 
    429     $mdoc = "$COPYRIGHT
    430 .Dd $TODAY
    431 .Dt " . uc($func->{'name'}) . " 3
    432 .Os
    433 .Sh NAME
    434 .Nm $func->{'name'}
    435 .Nd $func->{'descr'}
    436 .Sh LIBRARY
    437 .Lb libpam
    438 .Sh SYNOPSIS
    439 .In sys/types.h
    440 .In security/pam_appl.h
    441 ";
    442     if ($func->{'name'} =~ m/_sm_/) {
    443 	$mdoc .= ".In security/pam_modules.h\n"
    444     }
    445     if ($func->{'name'} =~ m/openpam/) {
    446 	$mdoc .= ".In security/openpam.h\n"
    447     }
    448     $mdoc .= ".Ft \"$func->{'type'}\"
    449 .Fn $func->{'name'} $func->{'args'}
    450 .Sh DESCRIPTION
    451 $func->{'man'}
    452 ";
    453     if ($func->{'type'} eq "int") {
    454 	$mdoc .= ".Sh RETURN VALUES
    455 The
    456 .Nm
    457 function returns one of the following values:
    458 .Bl -tag -width 18n
    459 ";
    460 	my @errors = @{$func->{'errors'}};
    461 	warn("$func->{'name'}(): no error specification\n")
    462 	    unless(@errors);
    463 	foreach (@errors) {
    464 	    $mdoc .= ".It Bq Er $_\n$PAMERR{$_}.\n";
    465 	}
    466 	$mdoc .= ".El\n";
    467     } else {
    468 	if ($func->{'type'} =~ m/\*$/) {
    469 	    $mdoc .= ".Sh RETURN VALUES
    470 The
    471 .Nm
    472 function returns
    473 .Dv NULL
    474 on failure.
    475 ";
    476 	}
    477     }
    478     $mdoc .= ".Sh SEE ALSO\n" . genxref($func->{'xref'});
    479     $mdoc .= ".Sh STANDARDS\n";
    480     if ($func->{'openpam'}) {
    481 	$mdoc .= "The
    482 .Nm
    483 function is an OpenPAM extension.
    484 ";
    485     } else {
    486 	$mdoc .= ".Rs
    487 .%T \"X/Open Single Sign-On Service (XSSO) - Pluggable Authentication Modules\"
    488 .%D \"June 1997\"
    489 .Re
    490 ";
    491     }
    492     $mdoc .= ".Sh AUTHORS
    493 The
    494 .Nm
    495 function and this manual page were developed for the
    496 .Fx
    497 Project by\n" . $AUTHORS{$func->{'author'} // 'THINKSEC_DARPA'} . "\n";
    498     $fn = "$func->{'name'}.3";
    499     if (open(FILE, ">", $fn)) {
    500 	print(FILE $mdoc);
    501 	close(FILE);
    502     } else {
    503 	warn("$fn: open(): $!\n");
    504     }
    505 }
    506 
    507 sub readproto($) {
    508     my $fn = shift;		# File name
    509 
    510     local *FILE;
    511     my %func;
    512 
    513     open(FILE, "<", "$fn")
    514 	or die("$fn: open(): $!\n");
    515     while (<FILE>) {
    516 	if (m/^\.Nm ((?:open)?pam_.*?)\s*$/) {
    517 	    $func{'Nm'} = $func{'Nm'} || $1;
    518 	} elsif (m/^\.Ft (\S.*?)\s*$/) {
    519 	    $func{'Ft'} = $func{'Ft'} || $1;
    520 	} elsif (m/^\.Fn (\S.*?)\s*$/) {
    521 	    $func{'Fn'} = $func{'Fn'} || $1;
    522 	}
    523     }
    524     close(FILE);
    525     if ($func{'Nm'}) {
    526 	$FUNCTIONS{$func{'Nm'}} = \%func;
    527     } else {
    528 	warn("No function found\n");
    529     }
    530 }
    531 
    532 sub gensummary($) {
    533     my $page = shift;		# Which page to produce
    534 
    535     local *FILE;
    536     my $upage;
    537     my $func;
    538     my %xref;
    539 
    540     open(FILE, ">", "$page.3")
    541 	or die("$page.3: $!\n");
    542 
    543     $page =~ m/(\w+)$/;
    544     $upage = uc($1);
    545     print FILE "$COPYRIGHT
    546 .Dd $TODAY
    547 .Dt $upage 3
    548 .Os
    549 .Sh NAME
    550 ";
    551     my @funcs = sort(keys(%FUNCTIONS));
    552     while ($func = shift(@funcs)) {
    553 	print FILE ".Nm $FUNCTIONS{$func}->{'Nm'}";
    554 	print FILE " ,"
    555 		if (@funcs);
    556 	print FILE "\n";
    557     }
    558     print FILE ".Nd Pluggable Authentication Modules Library
    559 .Sh LIBRARY
    560 .Lb libpam
    561 .Sh SYNOPSIS\n";
    562     if ($page eq 'pam') {
    563 	print FILE ".In security/pam_appl.h\n";
    564     } else {
    565 	print FILE ".In security/openpam.h\n";
    566     }
    567     foreach $func (sort(keys(%FUNCTIONS))) {
    568 	print FILE ".Ft $FUNCTIONS{$func}->{'Ft'}\n";
    569 	print FILE ".Fn $FUNCTIONS{$func}->{'Fn'}\n";
    570     }
    571     while (<STDIN>) {
    572 	if (m/^\.Xr (\S+)\s*(\d)\s*$/) {
    573 	    ++$xref{int($2)}->{$1};
    574 	}
    575 	print FILE $_;
    576     }
    577 
    578     if ($page eq 'pam') {
    579 	print FILE ".Sh RETURN VALUES
    580 The following return codes are defined by
    581 .In security/pam_constants.h :
    582 .Bl -tag -width 18n
    583 ";
    584 	foreach (sort(keys(%PAMERR))) {
    585 	    print FILE ".It Bq Er $_\n$PAMERR{$_}.\n";
    586 	}
    587 	print FILE ".El\n";
    588     }
    589     print FILE ".Sh SEE ALSO
    590 ";
    591     if ($page eq 'pam') {
    592 	++$xref{3}->{'openpam'};
    593     }
    594     foreach $func (keys(%FUNCTIONS)) {
    595 	++$xref{3}->{$func};
    596     }
    597     print FILE genxref(\%xref);
    598     print FILE ".Sh STANDARDS
    599 .Rs
    600 .%T \"X/Open Single Sign-On Service (XSSO) - Pluggable Authentication Modules\"
    601 .%D \"June 1997\"
    602 .Re
    603 .Sh AUTHORS
    604 The OpenPAM library and this manual page were developed for the
    605 .Fx
    606 Project by ThinkSec AS and Network Associates Laboratories, the
    607 Security Research Division of Network Associates, Inc.\\& under
    608 DARPA/SPAWAR contract N66001-01-C-8035
    609 .Pq Dq CBOSS ,
    610 as part of the DARPA CHATS research program.
    611 ";
    612     close(FILE);
    613 }
    614 
    615 sub usage() {
    616 
    617     print(STDERR "usage: gendoc [-op] source [...]\n");
    618     exit(1);
    619 }
    620 
    621 MAIN:{
    622     my %opts;
    623 
    624     usage()
    625 	unless (@ARGV && getopts("op", \%opts));
    626     setlocale(LC_ALL, "en_US.UTF-8");
    627     $TODAY = strftime("%B %e, %Y", localtime(time()));
    628     $TODAY =~ s,\s+, ,g;
    629     if ($opts{'o'} || $opts{'p'}) {
    630 	foreach my $fn (@ARGV) {
    631 	    readproto($fn);
    632 	}
    633 	gensummary('openpam')
    634 	    if ($opts{'o'});
    635 	gensummary('pam')
    636 	    if ($opts{'p'});
    637     } else {
    638 	foreach my $fn (@ARGV) {
    639 	    my $func = parse_source($fn);
    640 	    gendoc($func)
    641 		if (defined($func));
    642 	}
    643     }
    644     exit(0);
    645 }
    646