xref: /freebsd/contrib/openpam/misc/gendoc.pl (revision 37a60314613bc8baf1a46d9981e7c2f674dbe1cd)
1#!/usr/bin/perl -w
2#-
3# Copyright (c) 2002-2003 Networks Associates Technology, Inc.
4# Copyright (c) 2004-2011 Dag-Erling Smørgrav
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
39use strict;
40use locale;
41use Fcntl;
42use Getopt::Std;
43use POSIX qw(locale_h strftime);
44use 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 Smørgrav
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
86Project by ThinkSec AS and Network Associates Laboratories, the
87Security Research Division of Network Associates, Inc.\\& under
88DARPA/SPAWAR contract N66001-01-C-8035
89.Pq Dq CBOSS ,
90as 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
128sub 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
378sub expand_errors($);
379sub 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
428sub dictionary_order($$) {
429    my ($a, $b) = @_;
430
431    $a =~ s/[^[:alpha:]]//g;
432    $b =~ s/[^[:alpha:]]//g;
433    $a cmp $b;
434}
435
436sub 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
453sub 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
511The
512.Fn $func->{'name'}
513function 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
522The
523.Fn $func->{'name'}
524function returns 0 on success and -1 on failure.
525";
526    } elsif ($func->{'type'} =~ m/\*$/) {
527	$mdoc .= ".Sh RETURN VALUES
528The
529.Fn $func->{'name'}
530function returns
531.Dv NULL
532on 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'}
542function 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
552The
553.Fn $func->{'name'}
554function 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
565sub 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
590sub 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
638The 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
662The OpenPAM library and this manual page were developed for the
663.Fx
664Project by ThinkSec AS and Network Associates Laboratories, the
665Security Research Division of Network Associates, Inc.\\& under
666DARPA/SPAWAR contract N66001-01-C-8035
667.Pq Dq CBOSS ,
668as part of the DARPA CHATS research program.
669.Pp
670The OpenPAM library is maintained by
671.An Dag-Erling Sm\\(/orgrav Aq des\@des.no .
672";
673    close(FILE);
674}
675
676sub usage() {
677
678    print(STDERR "usage: gendoc [-op] source [...]\n");
679    exit(1);
680}
681
682MAIN:{
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