]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - contrib/openpam/misc/gendoc.pl
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / contrib / openpam / misc / gendoc.pl
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
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 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
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 }