]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/openpam/misc/gendoc.pl
MFV r353143 (phillip):
[FreeBSD/FreeBSD.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-2017 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 # $OpenPAM: gendoc.pl 938 2017-04-30 21:34:42Z des $
37 #
38
39 use strict;
40 use warnings;
41 use open qw(:utf8);
42 use utf8;
43 use Fcntl;
44 use Getopt::Std;
45 use POSIX qw(strftime);
46 use vars qw(%AUTHORS $TODAY %FUNCTIONS %PAMERR);
47
48 %AUTHORS = (
49     THINKSEC => "developed for the
50 .Fx
51 Project by ThinkSec AS and Network Associates Laboratories, the
52 Security Research Division of Network Associates, Inc.\\& under
53 DARPA/SPAWAR contract N66001-01-C-8035
54 .Pq Dq CBOSS ,
55 as part of the DARPA CHATS research program.
56 .Pp
57 The OpenPAM library is maintained by
58 .An Dag-Erling Sm\\(/orgrav Aq Mt des\@des.no .",
59     UIO => "developed for the University of Oslo by
60 .An Dag-Erling Sm\\(/orgrav Aq Mt des\@des.no .",
61     DES => "developed by
62 .An Dag-Erling Sm\\(/orgrav Aq Mt des\@des.no .",
63 );
64
65 %PAMERR = (
66     PAM_SUCCESS                 => "Success",
67     PAM_OPEN_ERR                => "Failed to load module",
68     PAM_SYMBOL_ERR              => "Invalid symbol",
69     PAM_SERVICE_ERR             => "Error in service module",
70     PAM_SYSTEM_ERR              => "System error",
71     PAM_BUF_ERR                 => "Memory buffer error",
72     PAM_CONV_ERR                => "Conversation failure",
73     PAM_PERM_DENIED             => "Permission denied",
74     PAM_MAXTRIES                => "Maximum number of tries exceeded",
75     PAM_AUTH_ERR                => "Authentication error",
76     PAM_NEW_AUTHTOK_REQD        => "New authentication token required",
77     PAM_CRED_INSUFFICIENT       => "Insufficient credentials",
78     PAM_AUTHINFO_UNAVAIL        => "Authentication information is unavailable",
79     PAM_USER_UNKNOWN            => "Unknown user",
80     PAM_CRED_UNAVAIL            => "Failed to retrieve user credentials",
81     PAM_CRED_EXPIRED            => "User credentials have expired",
82     PAM_CRED_ERR                => "Failed to set user credentials",
83     PAM_ACCT_EXPIRED            => "User account has expired",
84     PAM_AUTHTOK_EXPIRED         => "Password has expired",
85     PAM_SESSION_ERR             => "Session failure",
86     PAM_AUTHTOK_ERR             => "Authentication token failure",
87     PAM_AUTHTOK_RECOVERY_ERR    => "Failed to recover old authentication token",
88     PAM_AUTHTOK_LOCK_BUSY       => "Authentication token lock busy",
89     PAM_AUTHTOK_DISABLE_AGING   => "Authentication token aging disabled",
90     PAM_NO_MODULE_DATA          => "Module data not found",
91     PAM_IGNORE                  => "Ignore this module",
92     PAM_ABORT                   => "General failure",
93     PAM_TRY_AGAIN               => "Try again",
94     PAM_MODULE_UNKNOWN          => "Unknown module type",
95     PAM_DOMAIN_UNKNOWN          => "Unknown authentication domain",
96     PAM_BAD_HANDLE              => "Invalid PAM handle",
97     PAM_BAD_ITEM                => "Unrecognized or restricted item",
98     PAM_BAD_FEATURE             => "Unrecognized or restricted feature",
99     PAM_BAD_CONSTANT            => "Bad constant",
100 );
101
102 sub parse_source($) {
103     my $fn = shift;
104
105     local *FILE;
106     my $source;
107     my $func;
108     my $descr;
109     my $type;
110     my $args;
111     my $argnames;
112     my $man;
113     my $inlist;
114     my $intaglist;
115     my $inliteral;
116     my $customrv;
117     my $deprecated;
118     my $experimental;
119     my $version;
120     my %xref;
121     my %errors;
122     my $author;
123
124     if ($fn !~ m,\.c$,) {
125         warn("$fn: not C source, ignoring\n");
126         return undef;
127     }
128
129     open(FILE, "<", "$fn")
130         or die("$fn: open(): $!\n");
131     $source = join('', <FILE>);
132     close(FILE);
133
134     return undef
135         if ($source =~ m/^ \* NOPARSE\s*$/m);
136
137     if ($source =~ m/(\$OpenPAM:[^\$]+\$)/) {
138         $version = $1;
139     }
140
141     $author = 'THINKSEC';
142     if ($source =~ s/^ \* AUTHOR\s+(\w*)\s*$//m) {
143         $author = $1;
144     }
145
146     if ($source =~ s/^ \* DEPRECATED\s*(\w*)\s*$//m) {
147         $deprecated = $1 // 0;
148     }
149
150     if ($source =~ s/^ \* EXPERIMENTAL\s*$//m) {
151         $experimental = 1;
152     }
153
154     $func = $fn;
155     $func =~ s,^(?:.*/)?([^/]+)\.c$,$1,;
156     if ($source !~ m,\n \* ([\S ]+)\n \*/\n\n([\S ]+)\n$func\((.*?)\)\n\{,s) {
157         warn("$fn: can't find $func\n");
158         return undef;
159     }
160     ($descr, $type, $args) = ($1, $2, $3);
161     $descr =~ s,^([A-Z][a-z]),lc($1),e;
162     $descr =~ s,[\.\s]*$,,;
163     while ($args =~ s/^((?:[^\(]|\([^\)]*\))*),\s*/$1\" \"/g) {
164         # nothing
165     }
166     $args =~ s/,\s+/, /gs;
167     $args = "\"$args\"";
168
169     %xref = (
170         3 => { 'pam' => 1 },
171     );
172
173     if ($type eq "int") {
174         foreach (split("\n", $source)) {
175             next unless (m/^ \*\t(!?PAM_[A-Z_]+|=[a-z_]+)\s*(.*?)\s*$/);
176             $errors{$1} = $2;
177         }
178         ++$xref{3}->{pam_strerror};
179     }
180
181     $argnames = $args;
182     # extract names of regular arguments
183     $argnames =~ s/\"[^\"]+\*?\b(\w+)\"/\"$1\"/g;
184     # extract names of function pointer arguments
185     $argnames =~ s/\"([\w\s\*]+)\(\*?(\w+)\)\([^\)]+\)\"/\"$2\"/g;
186     # escape metacharacters (there shouldn't be any, but...)
187     $argnames =~ s/([\|\[\]\(\)\.\*\+\?])/\\$1/g;
188     # separate argument names with |
189     $argnames =~ s/\" \"/|/g;
190     # and surround with ()
191     $argnames =~ s/^\"(.*)\"$/$1/;
192     # $argnames is now a regexp that matches argument names
193     $inliteral = $inlist = $intaglist = 0;
194     foreach (split("\n", $source)) {
195         s/\s*$//;
196         if (!defined($man)) {
197             if (m/^\/\*\*$/) {
198                 $man = "";
199             }
200             next;
201         }
202         last if (m/^ \*\/$/);
203         s/^ \* ?//;
204         s/\\(.)/$1/gs;
205         if (m/^$/) {
206             # paragraph separator
207             if ($inlist || $intaglist) {
208                 # either a blank line between list items, or a blank
209                 # line after the final list item.  The latter case
210                 # will be handled further down.
211                 next;
212             }
213             if ($man =~ m/\n\.Sh [^\n]+\n$/s) {
214                 # a blank line after a section header
215                 next;
216             }
217             if ($man ne "" && $man !~ m/\.Pp\n$/s) {
218                 if ($inliteral) {
219                     $man .= "\0\n";
220                 } else {
221                     $man .= ".Pp\n";
222                 }
223             }
224             next;
225         }
226         if (m/^>(\w+)(\s+\d)?$/) {
227             # "see also" cross-reference
228             my ($page, $sect) = ($1, $2 ? int($2) : 3);
229             ++$xref{$sect}->{$page};
230             next;
231         }
232         if (s/^([A-Z][0-9A-Z -]+)$/.Sh $1/) {
233             if ($1 eq "RETURN VALUES") {
234                 $customrv = $1;
235             }
236             $man =~ s/\n\.Pp$/\n/s;
237             $man .= "$_\n";
238             next;
239         }
240         if (s/^\s+-\s+//) {
241             # item in bullet list
242             if ($inliteral) {
243                 $man .= ".Ed\n";
244                 $inliteral = 0;
245             }
246             if ($intaglist) {
247                 $man .= ".El\n.Pp\n";
248                 $intaglist = 0;
249             }
250             if (!$inlist) {
251                 $man =~ s/\.Pp\n$//s;
252                 $man .= ".Bl -bullet\n";
253                 $inlist = 1;
254             }
255             $man .= ".It\n";
256             # fall through
257         } elsif (s/^\s+(\S+):\s*/.It $1/) {
258             # item in tag list
259             if ($inliteral) {
260                 $man .= ".Ed\n";
261                 $inliteral = 0;
262             }
263             if ($inlist) {
264                 $man .= ".El\n.Pp\n";
265                 $inlist = 0;
266             }
267             if (!$intaglist) {
268                 $man =~ s/\.Pp\n$//s;
269                 $man .= ".Bl -tag -width 18n\n";
270                 $intaglist = 1;
271             }
272             s/^\.It [=;]([A-Za-z][0-9A-Za-z_]+)$/.It Dv $1/gs;
273             $man .= "$_\n";
274             next;
275         } elsif (($inlist || $intaglist) && m/^\S/) {
276             # regular text after list
277             $man .= ".El\n.Pp\n";
278             $inlist = $intaglist = 0;
279         } elsif ($inliteral && m/^\S/) {
280             # regular text after literal section
281             $man .= ".Ed\n";
282             $inliteral = 0;
283         } elsif ($inliteral) {
284             # additional text within literal section
285             $man .= "$_\n";
286             next;
287         } elsif ($inlist || $intaglist) {
288             # additional text within list
289             s/^\s+//;
290         } elsif (m/^\s+/) {
291             # new literal section
292             $man .= ".Bd -literal\n";
293             $inliteral = 1;
294             $man .= "$_\n";
295             next;
296         }
297         s/\s*=($func)\b\s*/\n.Fn $1\n/gs;
298         s/\s*=($argnames)\b\s*/\n.Fa $1\n/gs;
299         s/\s*=((?:enum|struct|union) \w+(?: \*)?)\b\s*/\n.Vt $1\n/gs;
300         s/\s*:([a-z][0-9a-z_]+)\b\s*/\n.Va $1\n/gs;
301         s/\s*;([a-z][0-9a-z_]+)\b\s*/\n.Dv $1\n/gs;
302         s/\s*=!([a-z][0-9a-z_]+)\b\s*/\n.Xr $1 3\n/gs;
303         while (s/\s*=([a-z][0-9a-z_]+)\b\s*/\n.Xr $1 3\n/s) {
304             ++$xref{3}->{$1};
305         }
306         s/\s*\"(?=\w)/\n.Do\n/gs;
307         s/\"(?!\w)\s*/\n.Dc\n/gs;
308         s/\s*=([A-Z][0-9A-Z_]+)\b\s*(?![\.,:;])/\n.Dv $1\n/gs;
309         s/\s*=([A-Z][0-9A-Z_]+)\b([\.,:;]+)\s*/\n.Dv $1 $2\n/gs;
310         s/\s*{([A-Z][a-z] .*?)}\s*/\n.$1\n/gs;
311         $man .= "$_\n";
312     }
313     if (defined($man)) {
314         if ($inlist || $intaglist) {
315             $man .= ".El\n";
316             $inlist = $intaglist = 0;
317         }
318         if ($inliteral) {
319             $man .= ".Ed\n";
320             $inliteral = 0;
321         }
322         $man =~ s/\%/\\&\%/gs;
323         $man =~ s/(\n\.[A-Z][a-z] [\w ]+)\n([.,:;-])\s+/$1 $2\n/gs;
324         $man =~ s/\s*$/\n/gm;
325         $man =~ s/\n+/\n/gs;
326         $man =~ s/\0//gs;
327         $man =~ s/\n\n\./\n\./gs;
328         chomp($man);
329     } else {
330         $man = "No description available.";
331     }
332
333     $FUNCTIONS{$func} = {
334         'source'        => $fn,
335         'version'       => $version,
336         'name'          => $func,
337         'descr'         => $descr,
338         'type'          => $type,
339         'args'          => $args,
340         'man'           => $man,
341         'xref'          => \%xref,
342         'errors'        => \%errors,
343         'author'        => $author,
344         'customrv'      => $customrv,
345         'deprecated'    => $deprecated,
346         'experimental'  => $experimental,
347     };
348     if ($source =~ m/^ \* NODOC\s*$/m) {
349         $FUNCTIONS{$func}->{nodoc} = 1;
350     }
351     if ($source !~ m/^ \* XSSO \d/m) {
352         $FUNCTIONS{$func}->{openpam} = 1;
353     }
354     expand_errors($FUNCTIONS{$func});
355     return $FUNCTIONS{$func};
356 }
357
358 sub expand_errors($);
359 sub expand_errors($) {
360     my $func = shift;           # Ref to function hash
361
362     my %errors;
363     my $ref;
364     my $fn;
365
366     if (defined($$func{recursed})) {
367         warn("$$func{name}(): loop in error spec\n");
368         return qw();
369     }
370     $$func{recursed} = 1;
371
372     foreach (keys %{$$func{errors}}) {
373         if (m/^(PAM_[A-Z_]+)$/) {
374             if (!defined($PAMERR{$1})) {
375                 warn("$$func{name}(): unrecognized error: $1\n");
376                 next;
377             }
378             $errors{$1} = $$func{errors}->{$_};
379         } elsif (m/^!(PAM_[A-Z_]+)$/) {
380             # treat negations separately
381         } elsif (m/^=([a-z_]+)$/) {
382             $ref = $1;
383             if (!defined($FUNCTIONS{$ref})) {
384                 $fn = $$func{source};
385                 $fn =~ s/$$func{name}/$ref/;
386                 parse_source($fn);
387             }
388             if (!defined($FUNCTIONS{$ref})) {
389                 warn("$$func{name}(): reference to unknown $ref()\n");
390                 next;
391             }
392             foreach (keys %{$FUNCTIONS{$ref}->{errors}}) {
393                 $errors{$_} //= $FUNCTIONS{$ref}->{errors}->{$_};
394             }
395         } else {
396             warn("$$func{name}(): invalid error specification: $_\n");
397         }
398     }
399     foreach (keys %{$$func{errors}}) {
400         if (m/^!(PAM_[A-Z_]+)$/) {
401             delete($errors{$1});
402         }
403     }
404     delete($$func{recursed});
405     $$func{errors} = \%errors;
406 }
407
408 sub dictionary_order($$) {
409     my ($a, $b) = @_;
410
411     $a =~ s/[^[:alpha:]]//g;
412     $b =~ s/[^[:alpha:]]//g;
413     $a cmp $b;
414 }
415
416 sub genxref($) {
417     my $xref = shift;           # References
418
419     my $mdoc = '';
420     my @refs = ();
421     foreach my $sect (sort(keys(%{$xref}))) {
422         foreach my $page (sort(dictionary_order keys(%{$xref->{$sect}}))) {
423             push(@refs, "$page $sect");
424         }
425     }
426     while ($_ = shift(@refs)) {
427         $mdoc .= ".Xr $_" .
428             (@refs ? " ,\n" : "\n");
429     }
430     return $mdoc;
431 }
432
433 sub gendoc($) {
434     my $func = shift;           # Ref to function hash
435
436     local *FILE;
437     my %errors;
438     my $mdoc;
439     my $fn;
440
441     return if defined($$func{nodoc});
442
443     $$func{source} =~ m/([^\/]+)$/;
444     $mdoc = ".\\\" Generated from $1 by gendoc.pl\n";
445     if ($$func{version}) {
446         $mdoc .= ".\\\" $$func{version}\n";
447     }
448     $mdoc .= ".Dd $TODAY
449 .Dt " . uc($$func{name}) . " 3
450 .Os
451 .Sh NAME
452 .Nm $$func{name}
453 .Nd $$func{descr}
454 ";
455     if ($func =~ m/^(?:open)?pam_/) {
456         $mdoc .= ".Sh LIBRARY
457 .Lb libpam
458 ";
459     }
460     $mdoc .= ".Sh SYNOPSIS
461 .In sys/types.h
462 ";
463     if ($$func{args} =~ m/\bFILE \*\b/) {
464         $mdoc .= ".In stdio.h\n";
465     }
466     if ($$func{name} =~ m/^(?:open)?pam/) {
467         $mdoc .= ".In security/pam_appl.h
468 ";
469     }
470     if ($$func{name} =~ m/_sm_/) {
471         $mdoc .= ".In security/pam_modules.h\n";
472     }
473     if ($$func{name} =~ m/openpam/) {
474         $mdoc .= ".In security/openpam.h\n";
475     }
476     $mdoc .= ".Ft \"$$func{type}\"
477 .Fn $$func{name} $$func{args}
478 .Sh DESCRIPTION
479 ";
480     if (defined($$func{deprecated})) {
481         $mdoc .= ".Bf Sy\n" .
482             "This function is deprecated and may be removed " .
483             "in a future release without further warning.\n";
484         if ($$func{deprecated}) {
485             $mdoc .= "The\n.Fn $$func{deprecated}\nfunction " .
486                 "may be used to achieve similar results.\n";
487         }
488         $mdoc .= ".Ef\n.Pp\n";
489     }
490     if ($$func{experimental}) {
491         $mdoc .= ".Bf Sy\n" .
492             "This function is experimental and may be modified or removed " .
493             "in a future release without prior warning.\n";
494         $mdoc .= ".Ef\n.Pp\n";
495     }
496     $mdoc .= "$$func{man}\n";
497     %errors = %{$$func{errors}};
498     if ($$func{customrv}) {
499         # leave it
500     } elsif ($$func{type} eq "int" && %errors) {
501         $mdoc .= ".Sh RETURN VALUES
502 The
503 .Fn $$func{name}
504 function returns one of the following values:
505 .Bl -tag -width 18n
506 ";
507         delete($errors{PAM_SUCCESS});
508         foreach ('PAM_SUCCESS', sort keys %errors) {
509             $mdoc .= ".It Bq Er $_\n" .
510                 ($errors{$_} || $PAMERR{$_}) .
511                 ".\n";
512         }
513         $mdoc .= ".El\n";
514     } elsif ($$func{type} eq "int") {
515         $mdoc .= ".Sh RETURN VALUES
516 The
517 .Fn $$func{name}
518 function returns 0 on success and -1 on failure.
519 ";
520     } elsif ($$func{type} =~ m/\*$/) {
521         $mdoc .= ".Sh RETURN VALUES
522 The
523 .Fn $$func{name}
524 function returns
525 .Dv NULL
526 on failure.
527 ";
528     } elsif ($$func{type} ne "void") {
529         warn("$$func{name}(): no error specification\n");
530     }
531     $mdoc .= ".Sh SEE ALSO\n" . genxref($$func{xref});
532     $mdoc .= ".Sh STANDARDS\n";
533     if ($$func{openpam}) {
534         $mdoc .= "The
535 .Fn $$func{name}
536 function is an OpenPAM extension.
537 ";
538     } else {
539         $mdoc .= ".Rs
540 .%T \"X/Open Single Sign-On Service (XSSO) - Pluggable Authentication Modules\"
541 .%D \"June 1997\"
542 .Re
543 ";
544     }
545     $mdoc .= ".Sh AUTHORS
546 The
547 .Fn $$func{name}
548 function and this manual page were\n";
549     $mdoc .= $AUTHORS{$$func{author} // 'THINKSEC_DARPA'} . "\n";
550     $fn = "$$func{name}.3";
551     if (open(FILE, ">", $fn)) {
552         print(FILE $mdoc);
553         close(FILE);
554     } else {
555         warn("$fn: open(): $!\n");
556     }
557 }
558
559 sub readproto($) {
560     my $fn = shift;             # File name
561
562     local *FILE;
563     my %func;
564
565     open(FILE, "<", "$fn")
566         or die("$fn: open(): $!\n");
567     while (<FILE>) {
568         if (m/^\.Nm ((?:(?:open)?pam)_.*?)\s*$/) {
569             $func{Nm} = $func{Nm} || $1;
570         } elsif (m/^\.Ft (\S.*?)\s*$/) {
571             $func{Ft} = $func{Ft} || $1;
572         } elsif (m/^\.Fn (\S.*?)\s*$/) {
573             $func{Fn} = $func{Fn} || $1;
574         }
575     }
576     close(FILE);
577     if ($func{Nm}) {
578         $FUNCTIONS{$func{Nm}} = \%func;
579     } else {
580         warn("No function found\n");
581     }
582 }
583
584 sub gensummary($) {
585     my $page = shift;           # Which page to produce
586
587     local *FILE;
588     my $upage;
589     my $func;
590     my %xref;
591
592     open(FILE, ">", "$page.3")
593         or die("$page.3: $!\n");
594
595     $page =~ m/(\w+)$/;
596     $upage = uc($1);
597     print FILE ".\\\" Generated by gendoc.pl
598 .Dd $TODAY
599 .Dt $upage 3
600 .Os
601 .Sh NAME
602 ";
603     my @funcs = sort(keys(%FUNCTIONS));
604     while ($func = shift(@funcs)) {
605         print FILE ".Nm $FUNCTIONS{$func}->{Nm}";
606         print FILE " ,"
607                 if (@funcs);
608         print FILE "\n";
609     }
610     print FILE ".Nd Pluggable Authentication Modules Library
611 .Sh LIBRARY
612 .Lb libpam
613 .Sh SYNOPSIS\n";
614     if ($page eq 'pam') {
615         print FILE ".In security/pam_appl.h\n";
616     } else {
617         print FILE ".In security/openpam.h\n";
618     }
619     foreach $func (sort(keys(%FUNCTIONS))) {
620         print FILE ".Ft $FUNCTIONS{$func}->{Ft}\n";
621         print FILE ".Fn $FUNCTIONS{$func}->{Fn}\n";
622     }
623     while (<STDIN>) {
624         if (m/^\.Xr (\S+)\s*(\d)\s*$/) {
625             ++$xref{int($2)}->{$1};
626         }
627         print FILE $_;
628     }
629
630     if ($page eq 'pam') {
631         print FILE ".Sh RETURN VALUES
632 The following return codes are defined by
633 .In security/pam_constants.h :
634 .Bl -tag -width 18n
635 ";
636         foreach (sort(keys(%PAMERR))) {
637             print FILE ".It Bq Er $_\n$PAMERR{$_}.\n";
638         }
639         print FILE ".El\n";
640     }
641     print FILE ".Sh SEE ALSO
642 ";
643     if ($page eq 'pam') {
644         ++$xref{3}->{openpam};
645     }
646     foreach $func (keys(%FUNCTIONS)) {
647         ++$xref{3}->{$func};
648     }
649     print FILE genxref(\%xref);
650     print FILE ".Sh STANDARDS
651 .Rs
652 .%T \"X/Open Single Sign-On Service (XSSO) - Pluggable Authentication Modules\"
653 .%D \"June 1997\"
654 .Re
655 ";
656     print FILE ".Sh AUTHORS
657 The OpenPAM library and this manual page were $AUTHORS{THINKSEC}
658 ";
659     close(FILE);
660 }
661
662 sub usage() {
663
664     print(STDERR "usage: gendoc [-op] source [...]\n");
665     exit(1);
666 }
667
668 MAIN:{
669     my %opts;
670
671     usage()
672         unless (@ARGV && getopts("op", \%opts));
673     $TODAY = strftime("%B %e, %Y", localtime(time()));
674     $TODAY =~ s,\s+, ,g;
675     if ($opts{o} || $opts{p}) {
676         foreach my $fn (@ARGV) {
677             readproto($fn);
678         }
679         gensummary('openpam')
680             if ($opts{o});
681         gensummary('pam')
682             if ($opts{p});
683     } else {
684         foreach my $fn (@ARGV) {
685             my $func = parse_source($fn);
686             gendoc($func)
687                 if (defined($func));
688         }
689     }
690     exit(0);
691 }