Home | History | Annotate | Line # | Download | only in misc
gendoc.pl revision 1.1.1.2
      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*=!([a-z_]+)\b\s*/\n.Xr $1 3\n/gs;
    324 	while (s/\s*=([a-z_]+)\b\s*/\n.Xr $1 3\n/s) {
    325 	    ++$xref{3}->{$1};
    326 	}
    327 	s/\s*\"(?=\w)/\n.Do\n/gs;
    328 	s/\"(?!\w)\s*/\n.Dc\n/gs;
    329 	s/\s*=([A-Z][A-Z_]+)\b\s*(?![\.,:;])/\n.Dv $1\n/gs;
    330 	s/\s*=([A-Z][A-Z_]+)\b([\.,:;]+)\s*/\n.Dv $1 $2\n/gs;
    331 	s/\s*{([A-Z][a-z] .*?)}\s*/\n.$1\n/gs;
    332 	$man .= "$_\n";
    333     }
    334     if (defined($man)) {
    335 	if ($inlist || $intaglist) {
    336 	    $man .= ".El\n";
    337 	    $inlist = $intaglist = 0;
    338 	}
    339 	if ($inliteral) {
    340 	    $man .= ".Ed\n";
    341 	    $inliteral = 0;
    342 	}
    343 	$man =~ s/\%/\\&\%/gs;
    344 	$man =~ s/(\n\.[A-Z][a-z] [\w ]+)\n([.,:;-])\s+/$1 $2\n/gs;
    345 	$man =~ s/\s*$/\n/gm;
    346 	$man =~ s/\n+/\n/gs;
    347 	$man =~ s/\0//gs;
    348 	$man =~ s/\n\n\./\n\./gs;
    349 	chomp($man);
    350     } else {
    351 	$man = "No description available.";
    352     }
    353 
    354     $FUNCTIONS{$func} = {
    355 	'source'	=> $fn,
    356 	'name'		=> $func,
    357 	'descr'		=> $descr,
    358 	'type'		=> $type,
    359 	'args'		=> $args,
    360 	'man'		=> $man,
    361 	'xref'		=> \%xref,
    362 	'errors'	=> \@errors,
    363 	'author'	=> $author,
    364 	'customrv'	=> $customrv,
    365 	'deprecated'	=> $deprecated,
    366 	'experimental'	=> $experimental,
    367     };
    368     if ($source =~ m/^ \* NODOC\s*$/m) {
    369 	$FUNCTIONS{$func}->{'nodoc'} = 1;
    370     }
    371     if ($source !~ m/^ \* XSSO \d/m) {
    372 	$FUNCTIONS{$func}->{'openpam'} = 1;
    373     }
    374     expand_errors($FUNCTIONS{$func});
    375     return $FUNCTIONS{$func};
    376 }
    377 
    378 sub expand_errors($);
    379 sub expand_errors($) {
    380     my $func = shift;		# Ref to function hash
    381 
    382     my %errors;
    383     my $ref;
    384     my $fn;
    385 
    386     if (defined($func->{'recursed'})) {
    387 	warn("$func->{'name'}(): loop in error spec\n");
    388 	return qw();
    389     }
    390     $func->{'recursed'} = 1;
    391 
    392     foreach (@{$func->{'errors'}}) {
    393 	if (m/^(PAM_[A-Z_]+)$/) {
    394 	    if (!defined($PAMERR{$1})) {
    395 		warn("$func->{'name'}(): unrecognized error: $1\n");
    396 		next;
    397 	    }
    398 	    $errors{$1} = 1;
    399 	} elsif (m/^!(PAM_[A-Z_]+)$/) {
    400 	    # treat negations separately
    401 	} elsif (m/^=([a-z_]+)$/) {
    402 	    $ref = $1;
    403 	    if (!defined($FUNCTIONS{$ref})) {
    404 		$fn = $func->{'source'};
    405 		$fn =~ s/$func->{'name'}/$ref/;
    406 		parse_source($fn);
    407 	    }
    408 	    if (!defined($FUNCTIONS{$ref})) {
    409 		warn("$func->{'name'}(): reference to unknown $ref()\n");
    410 		next;
    411 	    }
    412 	    foreach (@{$FUNCTIONS{$ref}->{'errors'}}) {
    413 		$errors{$_} = 1;
    414 	    }
    415 	} else {
    416 	    warn("$func->{'name'}(): invalid error specification: $_\n");
    417 	}
    418     }
    419     foreach (@{$func->{'errors'}}) {
    420 	if (m/^!(PAM_[A-Z_]+)$/) {
    421 	    delete($errors{$1});
    422 	}
    423     }
    424     delete($func->{'recursed'});
    425     $func->{'errors'} = [ sort(keys(%errors)) ];
    426 }
    427 
    428 sub dictionary_order($$) {
    429     my ($a, $b) = @_;
    430 
    431     $a =~ s/[^[:alpha:]]//g;
    432     $b =~ s/[^[:alpha:]]//g;
    433     $a cmp $b;
    434 }
    435 
    436 sub genxref($) {
    437     my $xref = shift;		# References
    438 
    439     my $mdoc = '';
    440     my @refs = ();
    441     foreach my $sect (sort(keys(%{$xref}))) {
    442 	foreach my $page (sort(dictionary_order keys(%{$xref->{$sect}}))) {
    443 	    push(@refs, "$page $sect");
    444 	}
    445     }
    446     while ($_ = shift(@refs)) {
    447 	$mdoc .= ".Xr $_" .
    448 	    (@refs ? " ,\n" : "\n");
    449     }
    450     return $mdoc;
    451 }
    452 
    453 sub gendoc($) {
    454     my $func = shift;		# Ref to function hash
    455 
    456     local *FILE;
    457     my $mdoc;
    458     my $fn;
    459 
    460     return if defined($func->{'nodoc'});
    461 
    462     $mdoc = "$COPYRIGHT
    463 .Dd $TODAY
    464 .Dt " . uc($func->{'name'}) . " 3
    465 .Os
    466 .Sh NAME
    467 .Nm $func->{'name'}
    468 .Nd $func->{'descr'}
    469 .Sh LIBRARY
    470 .Lb libpam
    471 .Sh SYNOPSIS
    472 .In sys/types.h
    473 ";
    474     if ($func->{'args'} =~ m/\bFILE \*\b/) {
    475 	$mdoc .= ".In stdio.h\n";
    476     }
    477     $mdoc .= ".In security/pam_appl.h
    478 ";
    479     if ($func->{'name'} =~ m/_sm_/) {
    480 	$mdoc .= ".In security/pam_modules.h\n";
    481     }
    482     if ($func->{'name'} =~ m/openpam/) {
    483 	$mdoc .= ".In security/openpam.h\n";
    484     }
    485     $mdoc .= ".Ft \"$func->{'type'}\"
    486 .Fn $func->{'name'} $func->{'args'}
    487 .Sh DESCRIPTION
    488 ";
    489     if (defined($func->{'deprecated'})) {
    490 	$mdoc .= ".Bf Sy\n" .
    491 	    "This function is deprecated and may be removed " .
    492 	    "in a future release without further warning.\n";
    493 	if ($func->{'deprecated'}) {
    494 	    $mdoc .= "The\n.Fn $func->{'deprecated'}\nfunction " .
    495 		"may be used to achieve similar results.\n";
    496 	}
    497 	$mdoc .= ".Ef\n.Pp\n";
    498     }
    499     if ($func->{'experimental'}) {
    500 	$mdoc .= ".Bf Sy\n" .
    501 	    "This function is experimental and may be modified or removed " .
    502 	    "in a future release without further warning.\n";
    503 	$mdoc .= ".Ef\n.Pp\n";
    504     }
    505     $mdoc .= "$func->{'man'}\n";
    506     my @errors = @{$func->{'errors'}};
    507     if ($func->{'customrv'}) {
    508 	# leave it
    509     } elsif ($func->{'type'} eq "int" && @errors) {
    510 	$mdoc .= ".Sh RETURN VALUES
    511 The
    512 .Fn $func->{'name'}
    513 function returns one of the following values:
    514 .Bl -tag -width 18n
    515 ";
    516 	foreach (@errors) {
    517 	    $mdoc .= ".It Bq Er $_\n$PAMERR{$_}.\n";
    518 	}
    519 	$mdoc .= ".El\n";
    520     } elsif ($func->{'type'} eq "int") {
    521 	$mdoc .= ".Sh RETURN VALUES
    522 The
    523 .Fn $func->{'name'}
    524 function returns 0 on success and -1 on failure.
    525 ";
    526     } elsif ($func->{'type'} =~ m/\*$/) {
    527 	$mdoc .= ".Sh RETURN VALUES
    528 The
    529 .Fn $func->{'name'}
    530 function returns
    531 .Dv NULL
    532 on failure.
    533 ";
    534     } elsif ($func->{'type'} ne "void") {
    535 	warn("$func->{'name'}(): no error specification\n");
    536     }
    537     $mdoc .= ".Sh SEE ALSO\n" . genxref($func->{'xref'});
    538     $mdoc .= ".Sh STANDARDS\n";
    539     if ($func->{'openpam'}) {
    540 	$mdoc .= "The
    541 .Fn $func->{'name'}
    542 function is an OpenPAM extension.
    543 ";
    544     } else {
    545 	$mdoc .= ".Rs
    546 .%T \"X/Open Single Sign-On Service (XSSO) - Pluggable Authentication Modules\"
    547 .%D \"June 1997\"
    548 .Re
    549 ";
    550     }
    551     $mdoc .= ".Sh AUTHORS
    552 The
    553 .Fn $func->{'name'}
    554 function and this manual page were\n";
    555     $mdoc .= $AUTHORS{$func->{'author'} // 'THINKSEC_DARPA'} . "\n";
    556     $fn = "$func->{'name'}.3";
    557     if (open(FILE, ">", $fn)) {
    558 	print(FILE $mdoc);
    559 	close(FILE);
    560     } else {
    561 	warn("$fn: open(): $!\n");
    562     }
    563 }
    564 
    565 sub readproto($) {
    566     my $fn = shift;		# File name
    567 
    568     local *FILE;
    569     my %func;
    570 
    571     open(FILE, "<", "$fn")
    572 	or die("$fn: open(): $!\n");
    573     while (<FILE>) {
    574 	if (m/^\.Nm ((?:open)?pam_.*?)\s*$/) {
    575 	    $func{'Nm'} = $func{'Nm'} || $1;
    576 	} elsif (m/^\.Ft (\S.*?)\s*$/) {
    577 	    $func{'Ft'} = $func{'Ft'} || $1;
    578 	} elsif (m/^\.Fn (\S.*?)\s*$/) {
    579 	    $func{'Fn'} = $func{'Fn'} || $1;
    580 	}
    581     }
    582     close(FILE);
    583     if ($func{'Nm'}) {
    584 	$FUNCTIONS{$func{'Nm'}} = \%func;
    585     } else {
    586 	warn("No function found\n");
    587     }
    588 }
    589 
    590 sub gensummary($) {
    591     my $page = shift;		# Which page to produce
    592 
    593     local *FILE;
    594     my $upage;
    595     my $func;
    596     my %xref;
    597 
    598     open(FILE, ">", "$page.3")
    599 	or die("$page.3: $!\n");
    600 
    601     $page =~ m/(\w+)$/;
    602     $upage = uc($1);
    603     print FILE "$COPYRIGHT
    604 .Dd $TODAY
    605 .Dt $upage 3
    606 .Os
    607 .Sh NAME
    608 ";
    609     my @funcs = sort(keys(%FUNCTIONS));
    610     while ($func = shift(@funcs)) {
    611 	print FILE ".Nm $FUNCTIONS{$func}->{'Nm'}";
    612 	print FILE " ,"
    613 		if (@funcs);
    614 	print FILE "\n";
    615     }
    616     print FILE ".Nd Pluggable Authentication Modules Library
    617 .Sh LIBRARY
    618 .Lb libpam
    619 .Sh SYNOPSIS\n";
    620     if ($page eq 'pam') {
    621 	print FILE ".In security/pam_appl.h\n";
    622     } else {
    623 	print FILE ".In security/openpam.h\n";
    624     }
    625     foreach $func (sort(keys(%FUNCTIONS))) {
    626 	print FILE ".Ft $FUNCTIONS{$func}->{'Ft'}\n";
    627 	print FILE ".Fn $FUNCTIONS{$func}->{'Fn'}\n";
    628     }
    629     while (<STDIN>) {
    630 	if (m/^\.Xr (\S+)\s*(\d)\s*$/) {
    631 	    ++$xref{int($2)}->{$1};
    632 	}
    633 	print FILE $_;
    634     }
    635 
    636     if ($page eq 'pam') {
    637 	print FILE ".Sh RETURN VALUES
    638 The following return codes are defined by
    639 .In security/pam_constants.h :
    640 .Bl -tag -width 18n
    641 ";
    642 	foreach (sort(keys(%PAMERR))) {
    643 	    print FILE ".It Bq Er $_\n$PAMERR{$_}.\n";
    644 	}
    645 	print FILE ".El\n";
    646     }
    647     print FILE ".Sh SEE ALSO
    648 ";
    649     if ($page eq 'pam') {
    650 	++$xref{3}->{'openpam'};
    651     }
    652     foreach $func (keys(%FUNCTIONS)) {
    653 	++$xref{3}->{$func};
    654     }
    655     print FILE genxref(\%xref);
    656     print FILE ".Sh STANDARDS
    657 .Rs
    658 .%T \"X/Open Single Sign-On Service (XSSO) - Pluggable Authentication Modules\"
    659 .%D \"June 1997\"
    660 .Re
    661 .Sh AUTHORS
    662 The OpenPAM library and this manual page were developed for the
    663 .Fx
    664 Project by ThinkSec AS and Network Associates Laboratories, the
    665 Security Research Division of Network Associates, Inc.\\& under
    666 DARPA/SPAWAR contract N66001-01-C-8035
    667 .Pq Dq CBOSS ,
    668 as part of the DARPA CHATS research program.
    669 .Pp
    670 The OpenPAM library is maintained by
    671 .An Dag-Erling Sm\\(/orgrav Aq des\@des.no .
    672 ";
    673     close(FILE);
    674 }
    675 
    676 sub usage() {
    677 
    678     print(STDERR "usage: gendoc [-op] source [...]\n");
    679     exit(1);
    680 }
    681 
    682 MAIN:{
    683     my %opts;
    684 
    685     usage()
    686 	unless (@ARGV && getopts("op", \%opts));
    687     setlocale(LC_ALL, "en_US.UTF-8");
    688     $TODAY = strftime("%B %e, %Y", localtime(time()));
    689     $TODAY =~ s,\s+, ,g;
    690     if ($opts{'o'} || $opts{'p'}) {
    691 	foreach my $fn (@ARGV) {
    692 	    readproto($fn);
    693 	}
    694 	gensummary('openpam')
    695 	    if ($opts{'o'});
    696 	gensummary('pam')
    697 	    if ($opts{'p'});
    698     } else {
    699 	foreach my $fn (@ARGV) {
    700 	    my $func = parse_source($fn);
    701 	    gendoc($func)
    702 		if (defined($func));
    703 	}
    704     }
    705     exit(0);
    706 }
    707