xref: /netbsd-src/external/bsd/openpam/dist/misc/gendoc.pl (revision 51c5f9b7c2b2cc93506078d2cab158634a65201f)
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 Mt 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*=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
379sub expand_errors($);
380sub 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
429sub dictionary_order($$) {
430    my ($a, $b) = @_;
431
432    $a =~ s/[^[:alpha:]]//g;
433    $b =~ s/[^[:alpha:]]//g;
434    $a cmp $b;
435}
436
437sub 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
454sub 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
515The
516.Fn $func->{'name'}
517function 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
526The
527.Fn $func->{'name'}
528function returns 0 on success and -1 on failure.
529";
530    } elsif ($func->{'type'} =~ m/\*$/) {
531	$mdoc .= ".Sh RETURN VALUES
532The
533.Fn $func->{'name'}
534function returns
535.Dv NULL
536on 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'}
546function 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
556The
557.Fn $func->{'name'}
558function 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
569sub 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
594sub 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
642The 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
666The OpenPAM library and this manual page were developed for the
667.Fx
668Project by ThinkSec AS and Network Associates Laboratories, the
669Security Research Division of Network Associates, Inc.\\& under
670DARPA/SPAWAR contract N66001-01-C-8035
671.Pq Dq CBOSS ,
672as part of the DARPA CHATS research program.
673.Pp
674The OpenPAM library is maintained by
675.An Dag-Erling Sm\\(/orgrav Aq Mt des\@des.no .
676";
677    close(FILE);
678}
679
680sub usage() {
681
682    print(STDERR "usage: gendoc [-op] source [...]\n");
683    exit(1);
684}
685
686MAIN:{
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