191100Sdes#!/usr/bin/perl -w
291100Sdes#-
3115619Sdes# Copyright (c) 2002-2003 Networks Associates Technology, Inc.
4228692Sdes# Copyright (c) 2004-2011 Dag-Erling Sm��rgrav
591100Sdes# All rights reserved.
691100Sdes#
791100Sdes# This software was developed for the FreeBSD Project by ThinkSec AS and
899158Sdes# Network Associates Laboratories, the Security Research Division of
999158Sdes# Network Associates, Inc.  under DARPA/SPAWAR contract N66001-01-C-8035
1099158Sdes# ("CBOSS"), as part of the DARPA CHATS research program.
1191100Sdes#
1291100Sdes# Redistribution and use in source and binary forms, with or without
1391100Sdes# modification, are permitted provided that the following conditions
1491100Sdes# are met:
1591100Sdes# 1. Redistributions of source code must retain the above copyright
1691100Sdes#    notice, this list of conditions and the following disclaimer.
1791100Sdes# 2. Redistributions in binary form must reproduce the above copyright
1891100Sdes#    notice, this list of conditions and the following disclaimer in the
1991100Sdes#    documentation and/or other materials provided with the distribution.
2091100Sdes# 3. The name of the author may not be used to endorse or promote
2191100Sdes#    products derived from this software without specific prior written
2291100Sdes#    permission.
2391100Sdes#
2491100Sdes# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2591100Sdes# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2691100Sdes# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2791100Sdes# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2891100Sdes# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2991100Sdes# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3091100Sdes# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3191100Sdes# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3291100Sdes# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3391100Sdes# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3491100Sdes# SUCH DAMAGE.
3591100Sdes#
36255376Sdes# $Id: gendoc.pl 736 2013-09-07 12:52:42Z des $
3791100Sdes#
3891100Sdes
3991100Sdesuse strict;
40255376Sdesuse warnings;
41255376Sdesuse open qw(:utf8);
42255376Sdesuse utf8;
4391100Sdesuse Fcntl;
4499158Sdesuse Getopt::Std;
45255376Sdesuse POSIX qw(strftime);
46255376Sdesuse vars qw(%AUTHORS $TODAY %FUNCTIONS %PAMERR);
4791100Sdes
48228692Sdes%AUTHORS = (
49236109Sdes    THINKSEC => "developed for the
50236109Sdes.Fx
51236109SdesProject by ThinkSec AS and Network Associates Laboratories, the
52228692SdesSecurity Research Division of Network Associates, Inc.\\& under
53228692SdesDARPA/SPAWAR contract N66001-01-C-8035
54228692Sdes.Pq Dq CBOSS ,
55255376Sdesas part of the DARPA CHATS research program.
56255376Sdes.Pp
57255376SdesThe OpenPAM library is maintained by
58255376Sdes.An Dag-Erling Sm\\(/orgrav Aq des\@des.no .",
59255376Sdes    UIO => "developed for the University of Oslo by
60255376Sdes.An Dag-Erling Sm\\(/orgrav Aq des\@des.no .",
61236109Sdes    DES => "developed by
62236109Sdes.An Dag-Erling Sm\\(/orgrav Aq des\@des.no .",
63228692Sdes);
64228692Sdes
6591100Sdes%PAMERR = (
6691100Sdes    PAM_SUCCESS			=> "Success",
6791100Sdes    PAM_OPEN_ERR		=> "Failed to load module",
6891100Sdes    PAM_SYMBOL_ERR		=> "Invalid symbol",
6991100Sdes    PAM_SERVICE_ERR		=> "Error in service module",
7091100Sdes    PAM_SYSTEM_ERR		=> "System error",
7191100Sdes    PAM_BUF_ERR			=> "Memory buffer error",
7291100Sdes    PAM_CONV_ERR		=> "Conversation failure",
7391100Sdes    PAM_PERM_DENIED		=> "Permission denied",
7491100Sdes    PAM_MAXTRIES		=> "Maximum number of tries exceeded",
7591100Sdes    PAM_AUTH_ERR		=> "Authentication error",
7691100Sdes    PAM_NEW_AUTHTOK_REQD	=> "New authentication token required",
7791100Sdes    PAM_CRED_INSUFFICIENT	=> "Insufficient credentials",
7891100Sdes    PAM_AUTHINFO_UNAVAIL	=> "Authentication information is unavailable",
7991100Sdes    PAM_USER_UNKNOWN		=> "Unknown user",
8091100Sdes    PAM_CRED_UNAVAIL		=> "Failed to retrieve user credentials",
8191100Sdes    PAM_CRED_EXPIRED		=> "User credentials have expired",
8291100Sdes    PAM_CRED_ERR		=> "Failed to set user credentials",
83141098Sdes    PAM_ACCT_EXPIRED		=> "User account has expired",
8491100Sdes    PAM_AUTHTOK_EXPIRED		=> "Password has expired",
8591100Sdes    PAM_SESSION_ERR		=> "Session failure",
8691100Sdes    PAM_AUTHTOK_ERR		=> "Authentication token failure",
8791100Sdes    PAM_AUTHTOK_RECOVERY_ERR	=> "Failed to recover old authentication token",
8891100Sdes    PAM_AUTHTOK_LOCK_BUSY	=> "Authentication token lock busy",
8991100Sdes    PAM_AUTHTOK_DISABLE_AGING	=> "Authentication token aging disabled",
9091100Sdes    PAM_NO_MODULE_DATA		=> "Module data not found",
9191100Sdes    PAM_IGNORE			=> "Ignore this module",
9291100Sdes    PAM_ABORT			=> "General failure",
9391100Sdes    PAM_TRY_AGAIN		=> "Try again",
9491100Sdes    PAM_MODULE_UNKNOWN		=> "Unknown module type",
9591100Sdes    PAM_DOMAIN_UNKNOWN		=> "Unknown authentication domain",
9691100Sdes);
9791100Sdes
9891100Sdessub parse_source($) {
9991100Sdes    my $fn = shift;
10091100Sdes
10191100Sdes    local *FILE;
10291100Sdes    my $source;
10391100Sdes    my $func;
10491100Sdes    my $descr;
10591100Sdes    my $type;
10691100Sdes    my $args;
10791100Sdes    my $argnames;
10891100Sdes    my $man;
10991100Sdes    my $inlist;
110228692Sdes    my $intaglist;
11191100Sdes    my $inliteral;
112236109Sdes    my $customrv;
113236109Sdes    my $deprecated;
114236109Sdes    my $experimental;
115255376Sdes    my $version;
11691100Sdes    my %xref;
11791100Sdes    my @errors;
118228692Sdes    my $author;
11991100Sdes
12091100Sdes    if ($fn !~ m,\.c$,) {
12191100Sdes	warn("$fn: not C source, ignoring\n");
12299158Sdes	return undef;
12391100Sdes    }
12491100Sdes
125228692Sdes    open(FILE, "<", "$fn")
12691100Sdes	or die("$fn: open(): $!\n");
12791100Sdes    $source = join('', <FILE>);
12891100Sdes    close(FILE);
12991100Sdes
13099158Sdes    return undef
13199158Sdes	if ($source =~ m/^ \* NOPARSE\s*$/m);
13291100Sdes
133255376Sdes    if ($source =~ m/(\$Id:[^\$]+\$)/) {
134255376Sdes	$version = $1;
135255376Sdes    }
136255376Sdes
137228692Sdes    $author = 'THINKSEC';
138236109Sdes    if ($source =~ s/^ \* AUTHOR\s+(\w*)\s*$//m) {
139228692Sdes	$author = $1;
140228692Sdes    }
141228692Sdes
142236109Sdes    if ($source =~ s/^ \* DEPRECATED\s*(\w*)\s*$//m) {
143236109Sdes	$deprecated = $1 // 0;
144236109Sdes    }
145236109Sdes
146236109Sdes    if ($source =~ s/^ \* EXPERIMENTAL\s*$//m) {
147236109Sdes	$experimental = 1;
148236109Sdes    }
149236109Sdes
15091100Sdes    $func = $fn;
15191100Sdes    $func =~ s,^(?:.*/)?([^/]+)\.c$,$1,;
15291100Sdes    if ($source !~ m,\n \* ([\S ]+)\n \*/\n\n([\S ]+)\n$func\((.*?)\)\n\{,s) {
15391100Sdes	warn("$fn: can't find $func\n");
15499158Sdes	return undef;
15591100Sdes    }
15691100Sdes    ($descr, $type, $args) = ($1, $2, $3);
15791100Sdes    $descr =~ s,^([A-Z][a-z]),lc($1),e;
15891100Sdes    $descr =~ s,[\.\s]*$,,;
15991100Sdes    while ($args =~ s/^((?:[^\(]|\([^\)]*\))*),\s*/$1\" \"/g) {
16091100Sdes	# nothing
16191100Sdes    }
16291100Sdes    $args =~ s/,\s+/, /gs;
16391100Sdes    $args = "\"$args\"";
16491100Sdes
16591100Sdes    %xref = (
166141098Sdes	3 => { 'pam' => 1 },
16791100Sdes    );
16891100Sdes
16991100Sdes    if ($type eq "int") {
17091100Sdes	foreach (split("\n", $source)) {
17191100Sdes	    next unless (m/^ \*\s+(!?PAM_[A-Z_]+|=[a-z_]+)\s*$/);
17291100Sdes	    push(@errors, $1);
17391100Sdes	}
174255376Sdes	++$xref{3}->{pam_strerror};
17591100Sdes    }
17691100Sdes
17791100Sdes    $argnames = $args;
178141098Sdes    # extract names of regular arguments
17991100Sdes    $argnames =~ s/\"[^\"]+\*?\b(\w+)\"/\"$1\"/g;
180141098Sdes    # extract names of function pointer arguments
181141098Sdes    $argnames =~ s/\"([\w\s\*]+)\(\*?(\w+)\)\([^\)]+\)\"/\"$2\"/g;
182141098Sdes    # escape metacharacters (there shouldn't be any, but...)
18391100Sdes    $argnames =~ s/([\|\[\]\(\)\.\*\+\?])/\\$1/g;
184141098Sdes    # separate argument names with |
18591100Sdes    $argnames =~ s/\" \"/|/g;
186141098Sdes    # and surround with ()
187236109Sdes    $argnames =~ s/^\"(.*)\"$/$1/;
188141098Sdes    # $argnames is now a regexp that matches argument names
189228692Sdes    $inliteral = $inlist = $intaglist = 0;
19091100Sdes    foreach (split("\n", $source)) {
19191100Sdes	s/\s*$//;
19291100Sdes	if (!defined($man)) {
19391100Sdes	    if (m/^\/\*\*$/) {
19491100Sdes		$man = "";
19591100Sdes	    }
19691100Sdes	    next;
19791100Sdes	}
19891100Sdes	last if (m/^ \*\/$/);
19991100Sdes	s/^ \* ?//;
20091100Sdes	s/\\(.)/$1/gs;
20191100Sdes	if (m/^$/) {
202228692Sdes	    # paragraph separator
203236109Sdes	    if ($inlist || $intaglist) {
204236109Sdes		# either a blank line between list items, or a blank
205236109Sdes		# line after the final list item.  The latter case
206236109Sdes		# will be handled further down.
207236109Sdes		next;
208236109Sdes	    }
209236109Sdes	    if ($man =~ m/\n\.Sh [^\n]+\n$/s) {
210236109Sdes		# a blank line after a section header
211236109Sdes		next;
212236109Sdes	    }
21391100Sdes	    if ($man ne "" && $man !~ m/\.Pp\n$/s) {
21491100Sdes		if ($inliteral) {
21591100Sdes		    $man .= "\0\n";
21691100Sdes		} else {
21791100Sdes		    $man .= ".Pp\n";
21891100Sdes		}
21991100Sdes	    }
22091100Sdes	    next;
22191100Sdes	}
222141098Sdes	if (m/^>(\w+)(\s+\d)?$/) {
223228692Sdes	    # "see also" cross-reference
224141098Sdes	    my ($page, $sect) = ($1, $2 ? int($2) : 3);
225141098Sdes	    ++$xref{$sect}->{$page};
22691100Sdes	    next;
22791100Sdes	}
228236109Sdes	if (s/^([A-Z][0-9A-Z -]+)$/.Sh $1/) {
229236109Sdes	    if ($1 eq "RETURN VALUES") {
230236109Sdes		$customrv = $1;
231236109Sdes	    }
232236109Sdes	    $man =~ s/\n\.Pp$/\n/s;
233236109Sdes	    $man .= "$_\n";
234236109Sdes	    next;
235236109Sdes	}
236228692Sdes	if (s/^\s+-\s+//) {
237228692Sdes	    # item in bullet list
23891100Sdes	    if ($inliteral) {
23991100Sdes		$man .= ".Ed\n";
24091100Sdes		$inliteral = 0;
24191100Sdes	    }
242228692Sdes	    if ($intaglist) {
243228692Sdes		$man .= ".El\n.Pp\n";
244228692Sdes		$intaglist = 0;
245228692Sdes	    }
24691100Sdes	    if (!$inlist) {
24791100Sdes		$man =~ s/\.Pp\n$//s;
248228692Sdes		$man .= ".Bl -bullet\n";
24991100Sdes		$inlist = 1;
25091100Sdes	    }
251228692Sdes	    $man .= ".It\n";
252228692Sdes	    # fall through
253228692Sdes	} elsif (s/^\s+(\S+):\s*/.It $1/) {
254228692Sdes	    # item in tag list
255228692Sdes	    if ($inliteral) {
256228692Sdes		$man .= ".Ed\n";
257228692Sdes		$inliteral = 0;
258228692Sdes	    }
259228692Sdes	    if ($inlist) {
260228692Sdes		$man .= ".El\n.Pp\n";
261228692Sdes		$inlist = 0;
262228692Sdes	    }
263228692Sdes	    if (!$intaglist) {
264228692Sdes		$man =~ s/\.Pp\n$//s;
265228692Sdes		$man .= ".Bl -tag -width 18n\n";
266228692Sdes		$intaglist = 1;
267228692Sdes	    }
268255376Sdes	    s/^\.It [=;]([A-Za-z][0-9A-Za-z_]+)$/.It Dv $1/gs;
26991100Sdes	    $man .= "$_\n";
27091100Sdes	    next;
271228692Sdes	} elsif (($inlist || $intaglist) && m/^\S/) {
272228692Sdes	    # regular text after list
27399158Sdes	    $man .= ".El\n.Pp\n";
274228692Sdes	    $inlist = $intaglist = 0;
27591100Sdes	} elsif ($inliteral && m/^\S/) {
276228692Sdes	    # regular text after literal section
27791100Sdes	    $man .= ".Ed\n";
27895908Sdes	    $inliteral = 0;
27991100Sdes	} elsif ($inliteral) {
280228692Sdes	    # additional text within literal section
28191100Sdes	    $man .= "$_\n";
28291100Sdes	    next;
283228692Sdes	} elsif ($inlist || $intaglist) {
284228692Sdes	    # additional text within list
28591100Sdes	    s/^\s+//;
28691100Sdes	} elsif (m/^\s+/) {
287228692Sdes	    # new literal section
28891100Sdes	    $man .= ".Bd -literal\n";
28991100Sdes	    $inliteral = 1;
29091100Sdes	    $man .= "$_\n";
29191100Sdes	    next;
29291100Sdes	}
293236109Sdes	s/\s*=($func)\b\s*/\n.Fn $1\n/gs;
294236109Sdes	s/\s*=($argnames)\b\s*/\n.Fa $1\n/gs;
29591100Sdes	s/\s*=(struct \w+(?: \*)?)\b\s*/\n.Vt $1\n/gs;
296255376Sdes	s/\s*:([a-z][0-9a-z_]+)\b\s*/\n.Va $1\n/gs;
297255376Sdes	s/\s*;([a-z][0-9a-z_]+)\b\s*/\n.Dv $1\n/gs;
298255376Sdes	s/\s*=!([a-z][0-9a-z_]+)\b\s*/\n.Xr $1 3\n/gs;
299255376Sdes	while (s/\s*=([a-z][0-9a-z_]+)\b\s*/\n.Xr $1 3\n/s) {
300141098Sdes	    ++$xref{3}->{$1};
30191100Sdes	}
30291100Sdes	s/\s*\"(?=\w)/\n.Do\n/gs;
30391100Sdes	s/\"(?!\w)\s*/\n.Dc\n/gs;
304255376Sdes	s/\s*=([A-Z][0-9A-Z_]+)\b\s*(?![\.,:;])/\n.Dv $1\n/gs;
305255376Sdes	s/\s*=([A-Z][0-9A-Z_]+)\b([\.,:;]+)\s*/\n.Dv $1 $2\n/gs;
30691100Sdes	s/\s*{([A-Z][a-z] .*?)}\s*/\n.$1\n/gs;
30791100Sdes	$man .= "$_\n";
30891100Sdes    }
30991100Sdes    if (defined($man)) {
310228692Sdes	if ($inlist || $intaglist) {
31195908Sdes	    $man .= ".El\n";
312228692Sdes	    $inlist = $intaglist = 0;
31395908Sdes	}
31495908Sdes	if ($inliteral) {
31595908Sdes	    $man .= ".Ed\n";
316228692Sdes	    $inliteral = 0;
31795908Sdes	}
318228692Sdes	$man =~ s/\%/\\&\%/gs;
319236109Sdes	$man =~ s/(\n\.[A-Z][a-z] [\w ]+)\n([.,:;-])\s+/$1 $2\n/gs;
32091100Sdes	$man =~ s/\s*$/\n/gm;
32191100Sdes	$man =~ s/\n+/\n/gs;
32291100Sdes	$man =~ s/\0//gs;
32395908Sdes	$man =~ s/\n\n\./\n\./gs;
32491100Sdes	chomp($man);
32591100Sdes    } else {
32691100Sdes	$man = "No description available.";
32791100Sdes    }
32891100Sdes
32991100Sdes    $FUNCTIONS{$func} = {
33099158Sdes	'source'	=> $fn,
331255376Sdes	'version'	=> $version,
33291100Sdes	'name'		=> $func,
33391100Sdes	'descr'		=> $descr,
33491100Sdes	'type'		=> $type,
33591100Sdes	'args'		=> $args,
33691100Sdes	'man'		=> $man,
33791100Sdes	'xref'		=> \%xref,
33891100Sdes	'errors'	=> \@errors,
339228692Sdes	'author'	=> $author,
340236109Sdes	'customrv'	=> $customrv,
341236109Sdes	'deprecated'	=> $deprecated,
342236109Sdes	'experimental'	=> $experimental,
34391100Sdes    };
34491100Sdes    if ($source =~ m/^ \* NODOC\s*$/m) {
345255376Sdes	$FUNCTIONS{$func}->{nodoc} = 1;
34691100Sdes    }
34791100Sdes    if ($source !~ m/^ \* XSSO \d/m) {
348255376Sdes	$FUNCTIONS{$func}->{openpam} = 1;
34991100Sdes    }
35099158Sdes    expand_errors($FUNCTIONS{$func});
35199158Sdes    return $FUNCTIONS{$func};
35291100Sdes}
35391100Sdes
35491100Sdessub expand_errors($);
35591100Sdessub expand_errors($) {
35691100Sdes    my $func = shift;		# Ref to function hash
35791100Sdes
35891100Sdes    my %errors;
35999158Sdes    my $ref;
36099158Sdes    my $fn;
36191100Sdes
362255376Sdes    if (defined($$func{recursed})) {
363255376Sdes	warn("$$func{name}(): loop in error spec\n");
36491100Sdes	return qw();
36591100Sdes    }
366255376Sdes    $$func{recursed} = 1;
36791100Sdes
368255376Sdes    foreach (@{$$func{errors}}) {
36991100Sdes	if (m/^(PAM_[A-Z_]+)$/) {
37091100Sdes	    if (!defined($PAMERR{$1})) {
371255376Sdes		warn("$$func{name}(): unrecognized error: $1\n");
37291100Sdes		next;
37391100Sdes	    }
37491100Sdes	    $errors{$1} = 1;
37591100Sdes	} elsif (m/^!(PAM_[A-Z_]+)$/) {
37691100Sdes	    # treat negations separately
37791100Sdes	} elsif (m/^=([a-z_]+)$/) {
37899158Sdes	    $ref = $1;
37999158Sdes	    if (!defined($FUNCTIONS{$ref})) {
380255376Sdes		$fn = $$func{source};
381255376Sdes		$fn =~ s/$$func{name}/$ref/;
38299158Sdes		parse_source($fn);
38399158Sdes	    }
38499158Sdes	    if (!defined($FUNCTIONS{$ref})) {
385255376Sdes		warn("$$func{name}(): reference to unknown $ref()\n");
38691100Sdes		next;
38791100Sdes	    }
388255376Sdes	    foreach (@{$FUNCTIONS{$ref}->{errors}}) {
38991100Sdes		$errors{$_} = 1;
39091100Sdes	    }
39191100Sdes	} else {
392255376Sdes	    warn("$$func{name}(): invalid error specification: $_\n");
39391100Sdes	}
39491100Sdes    }
395255376Sdes    foreach (@{$$func{errors}}) {
39691100Sdes	if (m/^!(PAM_[A-Z_]+)$/) {
39791100Sdes	    delete($errors{$1});
39891100Sdes	}
39991100Sdes    }
400255376Sdes    delete($$func{recursed});
401255376Sdes    $$func{errors} = [ sort(keys(%errors)) ];
40291100Sdes}
40391100Sdes
404147455Sdessub dictionary_order($$) {
405147455Sdes    my ($a, $b) = @_;
406147455Sdes
407147455Sdes    $a =~ s/[^[:alpha:]]//g;
408147455Sdes    $b =~ s/[^[:alpha:]]//g;
409147455Sdes    $a cmp $b;
410147455Sdes}
411147455Sdes
412141098Sdessub genxref($) {
413141098Sdes    my $xref = shift;		# References
414141098Sdes
415141098Sdes    my $mdoc = '';
416141098Sdes    my @refs = ();
417141098Sdes    foreach my $sect (sort(keys(%{$xref}))) {
418147455Sdes	foreach my $page (sort(dictionary_order keys(%{$xref->{$sect}}))) {
419141098Sdes	    push(@refs, "$page $sect");
420141098Sdes	}
421141098Sdes    }
422141098Sdes    while ($_ = shift(@refs)) {
423141098Sdes	$mdoc .= ".Xr $_" .
424141098Sdes	    (@refs ? " ,\n" : "\n");
425141098Sdes    }
426141098Sdes    return $mdoc;
427141098Sdes}
428141098Sdes
42991100Sdessub gendoc($) {
43091100Sdes    my $func = shift;		# Ref to function hash
43191100Sdes
43291100Sdes    local *FILE;
43391100Sdes    my $mdoc;
43491100Sdes    my $fn;
43591100Sdes
436255376Sdes    return if defined($$func{nodoc});
43791100Sdes
438255376Sdes    $$func{source} =~ m/([^\/]+)$/;
439255376Sdes    $mdoc = ".\\\" Generated from $1 by gendoc.pl\n";
440255376Sdes    if ($$func{version}) {
441255376Sdes	$mdoc .= ".\\\" $$func{version}\n";
442255376Sdes    }
443255376Sdes    $mdoc .= ".Dd $TODAY
444255376Sdes.Dt " . uc($$func{name}) . " 3
44591100Sdes.Os
44691100Sdes.Sh NAME
447255376Sdes.Nm $$func{name}
448255376Sdes.Nd $$func{descr}
44991100Sdes.Sh LIBRARY
45091100Sdes.Lb libpam
45191100Sdes.Sh SYNOPSIS
452108794Sdes.In sys/types.h
45391100Sdes";
454255376Sdes    if ($$func{args} =~ m/\bFILE \*\b/) {
455236109Sdes	$mdoc .= ".In stdio.h\n";
456236109Sdes    }
457236109Sdes    $mdoc .= ".In security/pam_appl.h
458236109Sdes";
459255376Sdes    if ($$func{name} =~ m/_sm_/) {
460236109Sdes	$mdoc .= ".In security/pam_modules.h\n";
46191100Sdes    }
462255376Sdes    if ($$func{name} =~ m/openpam/) {
463236109Sdes	$mdoc .= ".In security/openpam.h\n";
46495908Sdes    }
465255376Sdes    $mdoc .= ".Ft \"$$func{type}\"
466255376Sdes.Fn $$func{name} $$func{args}
46791100Sdes.Sh DESCRIPTION
46891100Sdes";
469255376Sdes    if (defined($$func{deprecated})) {
470236109Sdes	$mdoc .= ".Bf Sy\n" .
471236109Sdes	    "This function is deprecated and may be removed " .
472236109Sdes	    "in a future release without further warning.\n";
473255376Sdes	if ($$func{deprecated}) {
474255376Sdes	    $mdoc .= "The\n.Fn $$func{deprecated}\nfunction " .
475236109Sdes		"may be used to achieve similar results.\n";
476236109Sdes	}
477236109Sdes	$mdoc .= ".Ef\n.Pp\n";
478236109Sdes    }
479255376Sdes    if ($$func{experimental}) {
480236109Sdes	$mdoc .= ".Bf Sy\n" .
481236109Sdes	    "This function is experimental and may be modified or removed " .
482255376Sdes	    "in a future release without prior warning.\n";
483236109Sdes	$mdoc .= ".Ef\n.Pp\n";
484236109Sdes    }
485255376Sdes    $mdoc .= "$$func{man}\n";
486255376Sdes    my @errors = @{$$func{errors}};
487255376Sdes    if ($$func{customrv}) {
488236109Sdes	# leave it
489255376Sdes    } elsif ($$func{type} eq "int" && @errors) {
49091100Sdes	$mdoc .= ".Sh RETURN VALUES
49191100SdesThe
492255376Sdes.Fn $$func{name}
49391100Sdesfunction returns one of the following values:
49491100Sdes.Bl -tag -width 18n
49591100Sdes";
49691100Sdes	foreach (@errors) {
49791100Sdes	    $mdoc .= ".It Bq Er $_\n$PAMERR{$_}.\n";
49891100Sdes	}
49991100Sdes	$mdoc .= ".El\n";
500255376Sdes    } elsif ($$func{type} eq "int") {
501236109Sdes	$mdoc .= ".Sh RETURN VALUES
50291100SdesThe
503255376Sdes.Fn $$func{name}
504236109Sdesfunction returns 0 on success and -1 on failure.
505236109Sdes";
506255376Sdes    } elsif ($$func{type} =~ m/\*$/) {
507236109Sdes	$mdoc .= ".Sh RETURN VALUES
508236109SdesThe
509255376Sdes.Fn $$func{name}
51091100Sdesfunction returns
51191100Sdes.Dv NULL
51291100Sdeson failure.
51391100Sdes";
514255376Sdes    } elsif ($$func{type} ne "void") {
515255376Sdes	warn("$$func{name}(): no error specification\n");
51691100Sdes    }
517255376Sdes    $mdoc .= ".Sh SEE ALSO\n" . genxref($$func{xref});
51891100Sdes    $mdoc .= ".Sh STANDARDS\n";
519255376Sdes    if ($$func{openpam}) {
52091100Sdes	$mdoc .= "The
521255376Sdes.Fn $$func{name}
52291100Sdesfunction is an OpenPAM extension.
52391100Sdes";
52491100Sdes    } else {
52591100Sdes	$mdoc .= ".Rs
52691100Sdes.%T \"X/Open Single Sign-On Service (XSSO) - Pluggable Authentication Modules\"
52791100Sdes.%D \"June 1997\"
52891100Sdes.Re
52991100Sdes";
53091100Sdes    }
53191100Sdes    $mdoc .= ".Sh AUTHORS
53291100SdesThe
533255376Sdes.Fn $$func{name}
534236109Sdesfunction and this manual page were\n";
535255376Sdes    $mdoc .= $AUTHORS{$$func{author} // 'THINKSEC_DARPA'} . "\n";
536255376Sdes    $fn = "$$func{name}.3";
537228692Sdes    if (open(FILE, ">", $fn)) {
53897241Sdes	print(FILE $mdoc);
53997241Sdes	close(FILE);
54095908Sdes    } else {
54195908Sdes	warn("$fn: open(): $!\n");
54295908Sdes    }
54391100Sdes}
54491100Sdes
54599158Sdessub readproto($) {
54699158Sdes    my $fn = shift;		# File name
54791100Sdes
54899158Sdes    local *FILE;
54999158Sdes    my %func;
55099158Sdes
551228692Sdes    open(FILE, "<", "$fn")
55299158Sdes	or die("$fn: open(): $!\n");
55399158Sdes    while (<FILE>) {
55499158Sdes	if (m/^\.Nm ((?:open)?pam_.*?)\s*$/) {
555255376Sdes	    $func{Nm} = $func{Nm} || $1;
55699158Sdes	} elsif (m/^\.Ft (\S.*?)\s*$/) {
557255376Sdes	    $func{Ft} = $func{Ft} || $1;
55899158Sdes	} elsif (m/^\.Fn (\S.*?)\s*$/) {
559255376Sdes	    $func{Fn} = $func{Fn} || $1;
56099158Sdes	}
56199158Sdes    }
56299158Sdes    close(FILE);
563255376Sdes    if ($func{Nm}) {
564255376Sdes	$FUNCTIONS{$func{Nm}} = \%func;
56599158Sdes    } else {
56699158Sdes	warn("No function found\n");
56799158Sdes    }
56899158Sdes}
56999158Sdes
57099158Sdessub gensummary($) {
57199158Sdes    my $page = shift;		# Which page to produce
57299158Sdes
57399158Sdes    local *FILE;
57499158Sdes    my $upage;
57591100Sdes    my $func;
57699158Sdes    my %xref;
57791100Sdes
578228692Sdes    open(FILE, ">", "$page.3")
57999158Sdes	or die("$page.3: $!\n");
58099158Sdes
581228692Sdes    $page =~ m/(\w+)$/;
582228692Sdes    $upage = uc($1);
583255376Sdes    print FILE ".\\\" Generated by gendoc.pl
58491100Sdes.Dd $TODAY
58599158Sdes.Dt $upage 3
58691100Sdes.Os
58791100Sdes.Sh NAME
58891100Sdes";
58991100Sdes    my @funcs = sort(keys(%FUNCTIONS));
59091100Sdes    while ($func = shift(@funcs)) {
591255376Sdes	print FILE ".Nm $FUNCTIONS{$func}->{Nm}";
59299158Sdes	print FILE " ,"
59399158Sdes		if (@funcs);
59499158Sdes	print FILE "\n";
59591100Sdes    }
59699158Sdes    print FILE ".Nd Pluggable Authentication Modules Library
59791100Sdes.Sh LIBRARY
59891100Sdes.Lb libpam
59999158Sdes.Sh SYNOPSIS\n";
60099158Sdes    if ($page eq 'pam') {
60199158Sdes	print FILE ".In security/pam_appl.h\n";
60299158Sdes    } else {
60399158Sdes	print FILE ".In security/openpam.h\n";
60499158Sdes    }
60591100Sdes    foreach $func (sort(keys(%FUNCTIONS))) {
606255376Sdes	print FILE ".Ft $FUNCTIONS{$func}->{Ft}\n";
607255376Sdes	print FILE ".Fn $FUNCTIONS{$func}->{Fn}\n";
60891100Sdes    }
60999158Sdes    while (<STDIN>) {
61099158Sdes	if (m/^\.Xr (\S+)\s*(\d)\s*$/) {
611141098Sdes	    ++$xref{int($2)}->{$1};
612115619Sdes	}
61399158Sdes	print FILE $_;
61499158Sdes    }
61599158Sdes
61699158Sdes    if ($page eq 'pam') {
61799158Sdes	print FILE ".Sh RETURN VALUES
61899158SdesThe following return codes are defined by
619141098Sdes.In security/pam_constants.h :
62091100Sdes.Bl -tag -width 18n
62191100Sdes";
62299158Sdes	foreach (sort(keys(%PAMERR))) {
62399158Sdes	    print FILE ".It Bq Er $_\n$PAMERR{$_}.\n";
62499158Sdes	}
62599158Sdes	print FILE ".El\n";
62691100Sdes    }
62799158Sdes    print FILE ".Sh SEE ALSO
62891100Sdes";
629141098Sdes    if ($page eq 'pam') {
630255376Sdes	++$xref{3}->{openpam};
631141098Sdes    }
63299158Sdes    foreach $func (keys(%FUNCTIONS)) {
633141098Sdes	++$xref{3}->{$func};
63491100Sdes    }
635141098Sdes    print FILE genxref(\%xref);
63699158Sdes    print FILE ".Sh STANDARDS
63791100Sdes.Rs
63891100Sdes.%T \"X/Open Single Sign-On Service (XSSO) - Pluggable Authentication Modules\"
63991100Sdes.%D \"June 1997\"
64091100Sdes.Re
64191100Sdes.Sh AUTHORS
64291100SdesThe OpenPAM library and this manual page were developed for the
643117610Sdes.Fx
644117610SdesProject by ThinkSec AS and Network Associates Laboratories, the
645147464SdesSecurity Research Division of Network Associates, Inc.\\& under
64699158SdesDARPA/SPAWAR contract N66001-01-C-8035
64791100Sdes.Pq Dq CBOSS ,
64891100Sdesas part of the DARPA CHATS research program.
649236109Sdes.Pp
650236109SdesThe OpenPAM library is maintained by
651236109Sdes.An Dag-Erling Sm\\(/orgrav Aq des\@des.no .
65299158Sdes";
65399158Sdes    close(FILE);
65491100Sdes}
65591100Sdes
65699158Sdessub usage() {
65799158Sdes
658228692Sdes    print(STDERR "usage: gendoc [-op] source [...]\n");
65999158Sdes    exit(1);
66099158Sdes}
66199158Sdes
66291100SdesMAIN:{
66399158Sdes    my %opts;
66499158Sdes
66599158Sdes    usage()
66699158Sdes	unless (@ARGV && getopts("op", \%opts));
66791100Sdes    $TODAY = strftime("%B %e, %Y", localtime(time()));
66891100Sdes    $TODAY =~ s,\s+, ,g;
669255376Sdes    if ($opts{o} || $opts{p}) {
67099158Sdes	foreach my $fn (@ARGV) {
67199158Sdes	    readproto($fn);
67299158Sdes	}
67399158Sdes	gensummary('openpam')
674255376Sdes	    if ($opts{o});
67599158Sdes	gensummary('pam')
676255376Sdes	    if ($opts{p});
67799158Sdes    } else {
67899158Sdes	foreach my $fn (@ARGV) {
67999158Sdes	    my $func = parse_source($fn);
68099158Sdes	    gendoc($func)
68199158Sdes		if (defined($func));
68299158Sdes	}
68391100Sdes    }
68499158Sdes    exit(0);
68591100Sdes}
686