Home | History | Annotate | Line # | Download | only in misc
gendoc.pl revision 1.2.8.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 599 2012-04-14 15:06:41Z 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 => "developed for the
     85 .Fx
     86 Project by ThinkSec AS and Network Associates Laboratories, the
     87 Security Research Division of Network Associates, Inc.\\& under
     88 DARPA/SPAWAR contract N66001-01-C-8035
     89 .Pq Dq CBOSS ,
     90 as part of the DARPA CHATS research program.",
     91     DES => "developed by
     92 .An Dag-Erling Sm\\(/orgrav Aq des\@des.no .",
     93 );
     94 
     95 %PAMERR = (
     96     PAM_SUCCESS			=> "Success",
     97     PAM_OPEN_ERR		=> "Failed to load module",
     98     PAM_SYMBOL_ERR		=> "Invalid symbol",
     99     PAM_SERVICE_ERR		=> "Error in service module",
    100     PAM_SYSTEM_ERR		=> "System error",
    101     PAM_BUF_ERR			=> "Memory buffer error",
    102     PAM_CONV_ERR		=> "Conversation failure",
    103     PAM_PERM_DENIED		=> "Permission denied",
    104     PAM_MAXTRIES		=> "Maximum number of tries exceeded",
    105     PAM_AUTH_ERR		=> "Authentication error",
    106     PAM_NEW_AUTHTOK_REQD	=> "New authentication token required",
    107     PAM_CRED_INSUFFICIENT	=> "Insufficient credentials",
    108     PAM_AUTHINFO_UNAVAIL	=> "Authentication information is unavailable",
    109     PAM_USER_UNKNOWN		=> "Unknown user",
    110     PAM_CRED_UNAVAIL		=> "Failed to retrieve user credentials",
    111     PAM_CRED_EXPIRED		=> "User credentials have expired",
    112     PAM_CRED_ERR		=> "Failed to set user credentials",
    113     PAM_ACCT_EXPIRED		=> "User account has expired",
    114     PAM_AUTHTOK_EXPIRED		=> "Password has expired",
    115     PAM_SESSION_ERR		=> "Session failure",
    116     PAM_AUTHTOK_ERR		=> "Authentication token failure",
    117     PAM_AUTHTOK_RECOVERY_ERR	=> "Failed to recover old authentication token",
    118     PAM_AUTHTOK_LOCK_BUSY	=> "Authentication token lock busy",
    119     PAM_AUTHTOK_DISABLE_AGING	=> "Authentication token aging disabled",
    120     PAM_NO_MODULE_DATA		=> "Module data not found",
    121     PAM_IGNORE			=> "Ignore this module",
    122     PAM_ABORT			=> "General failure",
    123     PAM_TRY_AGAIN		=> "Try again",
    124     PAM_MODULE_UNKNOWN		=> "Unknown module type",
    125     PAM_DOMAIN_UNKNOWN		=> "Unknown authentication domain",
    126 );
    127 
    128 sub parse_source($) {
    129     my $fn = shift;
    130 
    131     local *FILE;
    132     my $source;
    133     my $func;
    134     my $descr;
    135     my $type;
    136     my $args;
    137     my $argnames;
    138     my $man;
    139     my $inlist;
    140     my $intaglist;
    141     my $inliteral;
    142     my $customrv;
    143     my $deprecated;
    144     my $experimental;
    145     my %xref;
    146     my @errors;
    147     my $author;
    148 
    149     if ($fn !~ m,\.c$,) {
    150 	warn("$fn: not C source, ignoring\n");
    151 	return undef;
    152     }
    153 
    154     open(FILE, "<", "$fn")
    155 	or die("$fn: open(): $!\n");
    156     $source = join('', <FILE>);
    157     close(FILE);
    158 
    159     return undef
    160 	if ($source =~ m/^ \* NOPARSE\s*$/m);
    161 
    162     $author = 'THINKSEC';
    163     if ($source =~ s/^ \* AUTHOR\s+(\w*)\s*$//m) {
    164 	$author = $1;
    165     }
    166 
    167     if ($source =~ s/^ \* DEPRECATED\s*(\w*)\s*$//m) {
    168 	$deprecated = $1 // 0;
    169     }
    170 
    171     if ($source =~ s/^ \* EXPERIMENTAL\s*$//m) {
    172 	$experimental = 1;
    173     }
    174 
    175     $func = $fn;
    176     $func =~ s,^(?:.*/)?([^/]+)\.c$,$1,;
    177     if ($source !~ m,\n \* ([\S ]+)\n \*/\n\n([\S ]+)\n$func\((.*?)\)\n\{,s) {
    178 	warn("$fn: can't find $func\n");
    179 	return undef;
    180     }
    181     ($descr, $type, $args) = ($1, $2, $3);
    182     $descr =~ s,^([A-Z][a-z]),lc($1),e;
    183     $descr =~ s,[\.\s]*$,,;
    184     while ($args =~ s/^((?:[^\(]|\([^\)]*\))*),\s*/$1\" \"/g) {
    185 	# nothing
    186     }
    187     $args =~ s/,\s+/, /gs;
    188     $args = "\"$args\"";
    189 
    190     %xref = (
    191 	3 => { 'pam' => 1 },
    192     );
    193 
    194     if ($type eq "int") {
    195 	foreach (split("\n", $source)) {
    196 	    next unless (m/^ \*\s+(!?PAM_[A-Z_]+|=[a-z_]+)\s*$/);
    197 	    push(@errors, $1);
    198 	}
    199 	++$xref{3}->{'pam_strerror'};
    200     }
    201 
    202     $argnames = $args;
    203     # extract names of regular arguments
    204     $argnames =~ s/\"[^\"]+\*?\b(\w+)\"/\"$1\"/g;
    205     # extract names of function pointer arguments
    206     $argnames =~ s/\"([\w\s\*]+)\(\*?(\w+)\)\([^\)]+\)\"/\"$2\"/g;
    207     # escape metacharacters (there shouldn't be any, but...)
    208     $argnames =~ s/([\|\[\]\(\)\.\*\+\?])/\\$1/g;
    209     # separate argument names with |
    210     $argnames =~ s/\" \"/|/g;
    211     # and surround with ()
    212     $argnames =~ s/^\"(.*)\"$/$1/;
    213     # $argnames is now a regexp that matches argument names
    214     $inliteral = $inlist = $intaglist = 0;
    215     foreach (split("\n", $source)) {
    216 	s/\s*$//;
    217 	if (!defined($man)) {
    218 	    if (m/^\/\*\*$/) {
    219 		$man = "";
    220 	    }
    221 	    next;
    222 	}
    223 	last if (m/^ \*\/$/);
    224 	s/^ \* ?//;
    225 	s/\\(.)/$1/gs;
    226 	if (m/^$/) {
    227 	    # paragraph separator
    228 	    if ($inlist || $intaglist) {
    229 		# either a blank line between list items, or a blank
    230 		# line after the final list item.  The latter case
    231 		# will be handled further down.
    232 		next;
    233 	    }
    234 	    if ($man =~ m/\n\.Sh [^\n]+\n$/s) {
    235 		# a blank line after a section header
    236 		next;
    237 	    }
    238 	    if ($man ne "" && $man !~ m/\.Pp\n$/s) {
    239 		if ($inliteral) {
    240 		    $man .= "\0\n";
    241 		} else {
    242 		    $man .= ".Pp\n";
    243 		}
    244 	    }
    245 	    next;
    246 	}
    247 	if (m/^>(\w+)(\s+\d)?$/) {
    248 	    # "see also" cross-reference
    249 	    my ($page, $sect) = ($1, $2 ? int($2) : 3);
    250 	    ++$xref{$sect}->{$page};
    251 	    next;
    252 	}
    253 	if (s/^([A-Z][0-9A-Z -]+)$/.Sh $1/) {
    254 	    if ($1 eq "RETURN VALUES") {
    255 		$customrv = $1;
    256 	    }
    257 	    $man =~ s/\n\.Pp$/\n/s;
    258 	    $man .= "$_\n";
    259 	    next;
    260 	}
    261 	if (s/^\s+-\s+//) {
    262 	    # item in bullet list
    263 	    if ($inliteral) {
    264 		$man .= ".Ed\n";
    265 		$inliteral = 0;
    266 	    }
    267 	    if ($intaglist) {
    268 		$man .= ".El\n.Pp\n";
    269 		$intaglist = 0;
    270 	    }
    271 	    if (!$inlist) {
    272 		$man =~ s/\.Pp\n$//s;
    273 		$man .= ".Bl -bullet\n";
    274 		$inlist = 1;
    275 	    }
    276 	    $man .= ".It\n";
    277 	    # fall through
    278 	} elsif (s/^\s+(\S+):\s*/.It $1/) {
    279 	    # item in tag list
    280 	    if ($inliteral) {
    281 		$man .= ".Ed\n";
    282 		$inliteral = 0;
    283 	    }
    284 	    if ($inlist) {
    285 		$man .= ".El\n.Pp\n";
    286 		$inlist = 0;
    287 	    }
    288 	    if (!$intaglist) {
    289 		$man =~ s/\.Pp\n$//s;
    290 		$man .= ".Bl -tag -width 18n\n";
    291 		$intaglist = 1;
    292 	    }
    293 	    s/^\.It =([A-Z][A-Z_]+)$/.It Dv $1/gs;
    294 	    $man .= "$_\n";
    295 	    next;
    296 	} elsif (($inlist || $intaglist) && m/^\S/) {
    297 	    # regular text after list
    298 	    $man .= ".El\n.Pp\n";
    299 	    $inlist = $intaglist = 0;
    300 	} elsif ($inliteral && m/^\S/) {
    301 	    # regular text after literal section
    302 	    $man .= ".Ed\n";
    303 	    $inliteral = 0;
    304 	} elsif ($inliteral) {
    305 	    # additional text within literal section
    306 	    $man .= "$_\n";
    307 	    next;
    308 	} elsif ($inlist || $intaglist) {
    309 	    # additional text within list
    310 	    s/^\s+//;
    311 	} elsif (m/^\s+/) {
    312 	    # new literal section
    313 	    $man .= ".Bd -literal\n";
    314 	    $inliteral = 1;
    315 	    $man .= "$_\n";
    316 	    next;
    317 	}
    318 	s/\s*=($func)\b\s*/\n.Fn $1\n/gs;
    319 	s/\s*=($argnames)\b\s*/\n.Fa $1\n/gs;
    320 	s/\s*=(struct \w+(?: \*)?)\b\s*/\n.Vt $1\n/gs;
    321 	s/\s*:([a-z_]+)\b\s*/\n.Va $1\n/gs;
    322 	s/\s*;([a-z_]+)\b\s*/\n.Dv $1\n/gs;
    323 	s/\s*=cleanup\s*/\n.Ar cleanup\n/gs;
    324 	s/\s*=!([a-z_]+)\b\s*/\n.Xr $1 3\n/gs;
    325 	while (s/\s*=([a-z_]+)\b\s*/\n.Xr $1 3\n/s) {
    326 	    ++$xref{3}->{$1};
    327 	}
    328 	s/\s*\"(?=\w)/\n.Do\n/gs;
    329 	s/\"(?!\w)\s*/\n.Dc\n/gs;
    330 	s/\s*=([A-Z][A-Z_]+)\b\s*(?![\.,:;])/\n.Dv $1\n/gs;
    331 	s/\s*=([A-Z][A-Z_]+)\b([\.,:;]+)\s*/\n.Dv $1 $2\n/gs;
    332 	s/\s*{([A-Z][a-z] .*?)}\s*/\n.$1\n/gs;
    333 	$man .= "$_\n";
    334     }
    335     if (defined($man)) {
    336 	if ($inlist || $intaglist) {
    337 	    $man .= ".El\n";
    338 	    $inlist = $intaglist = 0;
    339 	}
    340 	if ($inliteral) {
    341 	    $man .= ".Ed\n";
    342 	    $inliteral = 0;
    343 	}
    344 	$man =~ s/\%/\\&\%/gs;
    345 	$man =~ s/(\n\.[A-Z][a-z] [\w ]+)\n([.,:;-])\s+/$1 $2\n/gs;
    346 	$man =~ s/\s*$/\n/gm;
    347 	$man =~ s/\n+/\n/gs;
    348 	$man =~ s/\0//gs;
    349 	$man =~ s/\n\n\./\n\./gs;
    350 	chomp($man);
    351     } else {
    352 	$man = "No description available.";
    353     }
    354 
    355     $FUNCTIONS{$func} = {
    356 	'source'	=> $fn,
    357 	'name'		=> $func,
    358 	'descr'		=> $descr,
    359 	'type'		=> $type,
    360 	'args'		=> $args,
    361 	'man'		=> $man,
    362 	'xref'		=> \%xref,
    363 	'errors'	=> \@errors,
    364 	'author'	=> $author,
    365 	'customrv'	=> $customrv,
    366 	'deprecated'	=> $deprecated,
    367 	'experimental'	=> $experimental,
    368     };
    369     if ($source =~ m/^ \* NODOC\s*$/m) {
    370 	$FUNCTIONS{$func}->{'nodoc'} = 1;
    371     }
    372     if ($source !~ m/^ \* XSSO \d/m) {
    373 	$FUNCTIONS{$func}->{'openpam'} = 1;
    374     }
    375     expand_errors($FUNCTIONS{$func});
    376     return $FUNCTIONS{$func};
    377 }
    378 
    379 sub expand_errors($);
    380 sub expand_errors($) {
    381     my $func = shift;		# Ref to function hash
    382 
    383     my %errors;
    384     my $ref;
    385     my $fn;
    386 
    387     if (defined($func->{'recursed'})) {
    388 	warn("$func->{'name'}(): loop in error spec\n");
    389 	return qw();
    390     }
    391     $func->{'recursed'} = 1;
    392 
    393     foreach (@{$func->{'errors'}}) {
    394 	if (m/^(PAM_[A-Z_]+)$/) {
    395 	    if (!defined($PAMERR{$1})) {
    396 		warn("$func->{'name'}(): unrecognized error: $1\n");
    397 		next;
    398 	    }
    399 	    $errors{$1} = 1;
    400 	} elsif (m/^!(PAM_[A-Z_]+)$/) {
    401 	    # treat negations separately
    402 	} elsif (m/^=([a-z_]+)$/) {
    403 	    $ref = $1;
    404 	    if (!defined($FUNCTIONS{$ref})) {
    405 		$fn = $func->{'source'};
    406 		$fn =~ s/$func->{'name'}/$ref/;
    407 		parse_source($fn);
    408 	    }
    409 	    if (!defined($FUNCTIONS{$ref})) {
    410 		warn("$func->{'name'}(): reference to unknown $ref()\n");
    411 		next;
    412 	    }
    413 	    foreach (@{$FUNCTIONS{$ref}->{'errors'}}) {
    414 		$errors{$_} = 1;
    415 	    }
    416 	} else {
    417 	    warn("$func->{'name'}(): invalid error specification: $_\n");
    418 	}
    419     }
    420     foreach (@{$func->{'errors'}}) {
    421 	if (m/^!(PAM_[A-Z_]+)$/) {
    422 	    delete($errors{$1});
    423 	}
    424     }
    425     delete($func->{'recursed'});
    426     $func->{'errors'} = [ sort(keys(%errors)) ];
    427 }
    428 
    429 sub dictionary_order($$) {
    430     my ($a, $b) = @_;
    431 
    432     $a =~ s/[^[:alpha:]]//g;
    433     $b =~ s/[^[:alpha:]]//g;
    434     $a cmp $b;
    435 }
    436 
    437 sub genxref($) {
    438     my $xref = shift;		# References
    439 
    440     my $mdoc = '';
    441     my @refs = ();
    442     foreach my $sect (sort(keys(%{$xref}))) {
    443 	foreach my $page (sort(dictionary_order keys(%{$xref->{$sect}}))) {
    444 	    push(@refs, "$page $sect");
    445 	}
    446     }
    447     while ($_ = shift(@refs)) {
    448 	$mdoc .= ".Xr $_" .
    449 	    (@refs ? " ,\n" : "\n");
    450     }
    451     return $mdoc;
    452 }
    453 
    454 sub gendoc($) {
    455     my $func = shift;		# Ref to function hash
    456 
    457     local *FILE;
    458     my $mdoc;
    459     my $fn;
    460 
    461     return if defined($func->{'nodoc'});
    462 
    463     $mdoc = ".\\\"\t\$".
    464 "NetBSD\$
    465 .\\\"
    466 $COPYRIGHT
    467 .Dd $TODAY
    468 .Dt " . uc($func->{'name'}) . " 3
    469 .Os
    470 .Sh NAME
    471 .Nm $func->{'name'}
    472 .Nd $func->{'descr'}
    473 .Sh LIBRARY
    474 .Lb libpam
    475 .Sh SYNOPSIS
    476 .In sys/types.h
    477 ";
    478     if ($func->{'args'} =~ m/\bFILE \*\b/) {
    479 	$mdoc .= ".In stdio.h\n";
    480     }
    481     $mdoc .= ".In security/pam_appl.h
    482 ";
    483     if ($func->{'name'} =~ m/_sm_/) {
    484 	$mdoc .= ".In security/pam_modules.h\n";
    485     }
    486     if ($func->{'name'} =~ m/openpam/) {
    487 	$mdoc .= ".In security/openpam.h\n";
    488     }
    489     $mdoc .= ".Ft \"$func->{'type'}\"
    490 .Fn $func->{'name'} $func->{'args'}
    491 .Sh DESCRIPTION
    492 ";
    493     if (defined($func->{'deprecated'})) {
    494 	$mdoc .= ".Bf Sy\n" .
    495 	    "This function is deprecated and may be removed " .
    496 	    "in a future release without further warning.\n";
    497 	if ($func->{'deprecated'}) {
    498 	    $mdoc .= "The\n.Fn $func->{'deprecated'}\nfunction " .
    499 		"may be used to achieve similar results.\n";
    500 	}
    501 	$mdoc .= ".Ef\n.Pp\n";
    502     }
    503     if ($func->{'experimental'}) {
    504 	$mdoc .= ".Bf Sy\n" .
    505 	    "This function is experimental and may be modified or removed " .
    506 	    "in a future release without further warning.\n";
    507 	$mdoc .= ".Ef\n.Pp\n";
    508     }
    509     $mdoc .= "$func->{'man'}\n";
    510     my @errors = @{$func->{'errors'}};
    511     if ($func->{'customrv'}) {
    512 	# leave it
    513     } elsif ($func->{'type'} eq "int" && @errors) {
    514 	$mdoc .= ".Sh RETURN VALUES
    515 The
    516 .Fn $func->{'name'}
    517 function returns one of the following values:
    518 .Bl -tag -width 18n
    519 ";
    520 	foreach (@errors) {
    521 	    $mdoc .= ".It Bq Er $_\n$PAMERR{$_}.\n";
    522 	}
    523 	$mdoc .= ".El\n";
    524     } elsif ($func->{'type'} eq "int") {
    525 	$mdoc .= ".Sh RETURN VALUES
    526 The
    527 .Fn $func->{'name'}
    528 function returns 0 on success and -1 on failure.
    529 ";
    530     } elsif ($func->{'type'} =~ m/\*$/) {
    531 	$mdoc .= ".Sh RETURN VALUES
    532 The
    533 .Fn $func->{'name'}
    534 function returns
    535 .Dv NULL
    536 on failure.
    537 ";
    538     } elsif ($func->{'type'} ne "void") {
    539 	warn("$func->{'name'}(): no error specification\n");
    540     }
    541     $mdoc .= ".Sh SEE ALSO\n" . genxref($func->{'xref'});
    542     $mdoc .= ".Sh STANDARDS\n";
    543     if ($func->{'openpam'}) {
    544 	$mdoc .= "The
    545 .Fn $func->{'name'}
    546 function is an OpenPAM extension.
    547 ";
    548     } else {
    549 	$mdoc .= ".Rs
    550 .%T \"X/Open Single Sign-On Service (XSSO) - Pluggable Authentication Modules\"
    551 .%D \"June 1997\"
    552 .Re
    553 ";
    554     }
    555     $mdoc .= ".Sh AUTHORS
    556 The
    557 .Fn $func->{'name'}
    558 function and this manual page were\n";
    559     $mdoc .= $AUTHORS{$func->{'author'} // 'THINKSEC_DARPA'} . "\n";
    560     $fn = "$func->{'name'}.3";
    561     if (open(FILE, ">", $fn)) {
    562 	print(FILE $mdoc);
    563 	close(FILE);
    564     } else {
    565 	warn("$fn: open(): $!\n");
    566     }
    567 }
    568 
    569 sub readproto($) {
    570     my $fn = shift;		# File name
    571 
    572     local *FILE;
    573     my %func;
    574 
    575     open(FILE, "<", "$fn")
    576 	or die("$fn: open(): $!\n");
    577     while (<FILE>) {
    578 	if (m/^\.Nm ((?:open)?pam_.*?)\s*$/) {
    579 	    $func{'Nm'} = $func{'Nm'} || $1;
    580 	} elsif (m/^\.Ft (\S.*?)\s*$/) {
    581 	    $func{'Ft'} = $func{'Ft'} || $1;
    582 	} elsif (m/^\.Fn (\S.*?)\s*$/) {
    583 	    $func{'Fn'} = $func{'Fn'} || $1;
    584 	}
    585     }
    586     close(FILE);
    587     if ($func{'Nm'}) {
    588 	$FUNCTIONS{$func{'Nm'}} = \%func;
    589     } else {
    590 	warn("No function found\n");
    591     }
    592 }
    593 
    594 sub gensummary($) {
    595     my $page = shift;		# Which page to produce
    596 
    597     local *FILE;
    598     my $upage;
    599     my $func;
    600     my %xref;
    601 
    602     open(FILE, ">", "$page.3")
    603 	or die("$page.3: $!\n");
    604 
    605     $page =~ m/(\w+)$/;
    606     $upage = uc($1);
    607     print FILE "$COPYRIGHT
    608 .Dd $TODAY
    609 .Dt $upage 3
    610 .Os
    611 .Sh NAME
    612 ";
    613     my @funcs = sort(keys(%FUNCTIONS));
    614     while ($func = shift(@funcs)) {
    615 	print FILE ".Nm $FUNCTIONS{$func}->{'Nm'}";
    616 	print FILE " ,"
    617 		if (@funcs);
    618 	print FILE "\n";
    619     }
    620     print FILE ".Nd Pluggable Authentication Modules Library
    621 .Sh LIBRARY
    622 .Lb libpam
    623 .Sh SYNOPSIS\n";
    624     if ($page eq 'pam') {
    625 	print FILE ".In security/pam_appl.h\n";
    626     } else {
    627 	print FILE ".In security/openpam.h\n";
    628     }
    629     foreach $func (sort(keys(%FUNCTIONS))) {
    630 	print FILE ".Ft $FUNCTIONS{$func}->{'Ft'}\n";
    631 	print FILE ".Fn $FUNCTIONS{$func}->{'Fn'}\n";
    632     }
    633     while (<STDIN>) {
    634 	if (m/^\.Xr (\S+)\s*(\d)\s*$/) {
    635 	    ++$xref{int($2)}->{$1};
    636 	}
    637 	print FILE $_;
    638     }
    639 
    640     if ($page eq 'pam') {
    641 	print FILE ".Sh RETURN VALUES
    642 The following return codes are defined by
    643 .In security/pam_constants.h :
    644 .Bl -tag -width 18n
    645 ";
    646 	foreach (sort(keys(%PAMERR))) {
    647 	    print FILE ".It Bq Er $_\n$PAMERR{$_}.\n";
    648 	}
    649 	print FILE ".El\n";
    650     }
    651     print FILE ".Sh SEE ALSO
    652 ";
    653     if ($page eq 'pam') {
    654 	++$xref{3}->{'openpam'};
    655     }
    656     foreach $func (keys(%FUNCTIONS)) {
    657 	++$xref{3}->{$func};
    658     }
    659     print FILE genxref(\%xref);
    660     print FILE ".Sh STANDARDS
    661 .Rs
    662 .%T \"X/Open Single Sign-On Service (XSSO) - Pluggable Authentication Modules\"
    663 .%D \"June 1997\"
    664 .Re
    665 .Sh AUTHORS
    666 The OpenPAM library and this manual page were developed for the
    667 .Fx
    668 Project by ThinkSec AS and Network Associates Laboratories, the
    669 Security Research Division of Network Associates, Inc.\\& under
    670 DARPA/SPAWAR contract N66001-01-C-8035
    671 .Pq Dq CBOSS ,
    672 as part of the DARPA CHATS research program.
    673 .Pp
    674 The OpenPAM library is maintained by
    675 .An Dag-Erling Sm\\(/orgrav Aq des\@des.no .
    676 ";
    677     close(FILE);
    678 }
    679 
    680 sub usage() {
    681 
    682     print(STDERR "usage: gendoc [-op] source [...]\n");
    683     exit(1);
    684 }
    685 
    686 MAIN:{
    687     my %opts;
    688 
    689     usage()
    690 	unless (@ARGV && getopts("op", \%opts));
    691     setlocale(LC_ALL, "en_US.UTF-8");
    692     $TODAY = strftime("%B %e, %Y", localtime(time()));
    693     $TODAY =~ s,\s+, ,g;
    694     if ($opts{'o'} || $opts{'p'}) {
    695 	foreach my $fn (@ARGV) {
    696 	    readproto($fn);
    697 	}
    698 	gensummary('openpam')
    699 	    if ($opts{'o'});
    700 	gensummary('pam')
    701 	    if ($opts{'p'});
    702     } else {
    703 	foreach my $fn (@ARGV) {
    704 	    my $func = parse_source($fn);
    705 	    gendoc($func)
    706 		if (defined($func));
    707 	}
    708     }
    709     exit(0);
    710 }
    711