xref: /netbsd-src/external/bsd/openpam/dist/misc/gendoc.pl (revision c38e7cc395b1472a774ff828e46123de44c628e9)
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
39use strict;
40use warnings;
41use open qw(:utf8);
42use utf8;
43use Fcntl;
44use Getopt::Std;
45use POSIX qw(strftime);
46use vars qw(%AUTHORS $TODAY %FUNCTIONS %PAMERR);
47
48%AUTHORS = (
49    THINKSEC => "developed for the
50.Fx
51Project by ThinkSec AS and Network Associates Laboratories, the
52Security Research Division of Network Associates, Inc.\\& under
53DARPA/SPAWAR contract N66001-01-C-8035
54.Pq Dq CBOSS ,
55as part of the DARPA CHATS research program.
56.Pp
57The 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
102sub 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
358sub expand_errors($);
359sub 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
408sub dictionary_order($$) {
409    my ($a, $b) = @_;
410
411    $a =~ s/[^[:alpha:]]//g;
412    $b =~ s/[^[:alpha:]]//g;
413    $a cmp $b;
414}
415
416sub 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
433sub 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    $mdoc = ".\\\"\t\$".
444"NetBSD\$
445.\\\"
446    $$func{source} =~ m/([^\/]+)$/;
447    $mdoc .= ".\\\" Generated from $1 by gendoc.pl\n";
448    if ($$func{version}) {
449	$mdoc .= ".\\\" $$func{version}\n";
450    }
451    $mdoc .= ".Dd $TODAY
452.Dt " . uc($$func{name}) . " 3
453.Os
454.Sh NAME
455.Nm $$func{name}
456.Nd $$func{descr}
457";
458    if ($func =~ m/^(?:open)?pam_/) {
459	$mdoc .= ".Sh LIBRARY
460.Lb libpam
461";
462    }
463    $mdoc .= ".Sh SYNOPSIS
464.In sys/types.h
465";
466    if ($$func{args} =~ m/\bFILE \*\b/) {
467	$mdoc .= ".In stdio.h\n";
468    }
469    if ($$func{name} =~ m/^(?:open)?pam/) {
470	$mdoc .= ".In security/pam_appl.h
471";
472    }
473    if ($$func{name} =~ m/_sm_/) {
474	$mdoc .= ".In security/pam_modules.h\n";
475    }
476    if ($$func{name} =~ m/openpam/) {
477	$mdoc .= ".In security/openpam.h\n";
478    }
479    $mdoc .= ".Ft \"$$func{type}\"
480.Fn $$func{name} $$func{args}
481.Sh DESCRIPTION
482";
483    if (defined($$func{deprecated})) {
484	$mdoc .= ".Bf Sy\n" .
485	    "This function is deprecated and may be removed " .
486	    "in a future release without further warning.\n";
487	if ($$func{deprecated}) {
488	    $mdoc .= "The\n.Fn $$func{deprecated}\nfunction " .
489		"may be used to achieve similar results.\n";
490	}
491	$mdoc .= ".Ef\n.Pp\n";
492    }
493    if ($$func{experimental}) {
494	$mdoc .= ".Bf Sy\n" .
495	    "This function is experimental and may be modified or removed " .
496	    "in a future release without prior warning.\n";
497	$mdoc .= ".Ef\n.Pp\n";
498    }
499    $mdoc .= "$$func{man}\n";
500    %errors = %{$$func{errors}};
501    if ($$func{customrv}) {
502	# leave it
503    } elsif ($$func{type} eq "int" && %errors) {
504	$mdoc .= ".Sh RETURN VALUES
505The
506.Fn $$func{name}
507function returns one of the following values:
508.Bl -tag -width 18n
509";
510	delete($errors{PAM_SUCCESS});
511	foreach ('PAM_SUCCESS', sort keys %errors) {
512	    $mdoc .= ".It Bq Er $_\n" .
513		($errors{$_} || $PAMERR{$_}) .
514		".\n";
515	}
516	$mdoc .= ".El\n";
517    } elsif ($$func{type} eq "int") {
518	$mdoc .= ".Sh RETURN VALUES
519The
520.Fn $$func{name}
521function returns 0 on success and -1 on failure.
522";
523    } elsif ($$func{type} =~ m/\*$/) {
524	$mdoc .= ".Sh RETURN VALUES
525The
526.Fn $$func{name}
527function returns
528.Dv NULL
529on failure.
530";
531    } elsif ($$func{type} ne "void") {
532	warn("$$func{name}(): no error specification\n");
533    }
534    $mdoc .= ".Sh SEE ALSO\n" . genxref($$func{xref});
535    $mdoc .= ".Sh STANDARDS\n";
536    if ($$func{openpam}) {
537	$mdoc .= "The
538.Fn $$func{name}
539function is an OpenPAM extension.
540";
541    } else {
542	$mdoc .= ".Rs
543.%T \"X/Open Single Sign-On Service (XSSO) - Pluggable Authentication Modules\"
544.%D \"June 1997\"
545.Re
546";
547    }
548    $mdoc .= ".Sh AUTHORS
549The
550.Fn $$func{name}
551function and this manual page were\n";
552    $mdoc .= $AUTHORS{$$func{author} // 'THINKSEC_DARPA'} . "\n";
553    $fn = "$$func{name}.3";
554    if (open(FILE, ">", $fn)) {
555	print(FILE $mdoc);
556	close(FILE);
557    } else {
558	warn("$fn: open(): $!\n");
559    }
560}
561
562sub readproto($) {
563    my $fn = shift;		# File name
564
565    local *FILE;
566    my %func;
567
568    open(FILE, "<", "$fn")
569	or die("$fn: open(): $!\n");
570    while (<FILE>) {
571	if (m/^\.Nm ((?:(?:open)?pam)_.*?)\s*$/) {
572	    $func{Nm} = $func{Nm} || $1;
573	} elsif (m/^\.Ft (\S.*?)\s*$/) {
574	    $func{Ft} = $func{Ft} || $1;
575	} elsif (m/^\.Fn (\S.*?)\s*$/) {
576	    $func{Fn} = $func{Fn} || $1;
577	}
578    }
579    close(FILE);
580    if ($func{Nm}) {
581	$FUNCTIONS{$func{Nm}} = \%func;
582    } else {
583	warn("No function found\n");
584    }
585}
586
587sub gensummary($) {
588    my $page = shift;		# Which page to produce
589
590    local *FILE;
591    my $upage;
592    my $func;
593    my %xref;
594
595    open(FILE, ">", "$page.3")
596	or die("$page.3: $!\n");
597
598    $page =~ m/(\w+)$/;
599    $upage = uc($1);
600    print FILE ".\\\" Generated by gendoc.pl
601.Dd $TODAY
602.Dt $upage 3
603.Os
604.Sh NAME
605";
606    my @funcs = sort(keys(%FUNCTIONS));
607    while ($func = shift(@funcs)) {
608	print FILE ".Nm $FUNCTIONS{$func}->{Nm}";
609	print FILE " ,"
610		if (@funcs);
611	print FILE "\n";
612    }
613    print FILE ".Nd Pluggable Authentication Modules Library
614.Sh LIBRARY
615.Lb libpam
616.Sh SYNOPSIS\n";
617    if ($page eq 'pam') {
618	print FILE ".In security/pam_appl.h\n";
619    } else {
620	print FILE ".In security/openpam.h\n";
621    }
622    foreach $func (sort(keys(%FUNCTIONS))) {
623	print FILE ".Ft $FUNCTIONS{$func}->{Ft}\n";
624	print FILE ".Fn $FUNCTIONS{$func}->{Fn}\n";
625    }
626    while (<STDIN>) {
627	if (m/^\.Xr (\S+)\s*(\d)\s*$/) {
628	    ++$xref{int($2)}->{$1};
629	}
630	print FILE $_;
631    }
632
633    if ($page eq 'pam') {
634	print FILE ".Sh RETURN VALUES
635The following return codes are defined by
636.In security/pam_constants.h :
637.Bl -tag -width 18n
638";
639	foreach (sort(keys(%PAMERR))) {
640	    print FILE ".It Bq Er $_\n$PAMERR{$_}.\n";
641	}
642	print FILE ".El\n";
643    }
644    print FILE ".Sh SEE ALSO
645";
646    if ($page eq 'pam') {
647	++$xref{3}->{openpam};
648    }
649    foreach $func (keys(%FUNCTIONS)) {
650	++$xref{3}->{$func};
651    }
652    print FILE genxref(\%xref);
653    print FILE ".Sh STANDARDS
654.Rs
655.%T \"X/Open Single Sign-On Service (XSSO) - Pluggable Authentication Modules\"
656.%D \"June 1997\"
657.Re
658";
659    print FILE ".Sh AUTHORS
660The OpenPAM library and this manual page were $AUTHORS{THINKSEC}
661";
662    close(FILE);
663}
664
665sub usage() {
666
667    print(STDERR "usage: gendoc [-op] source [...]\n");
668    exit(1);
669}
670
671MAIN:{
672    my %opts;
673
674    usage()
675	unless (@ARGV && getopts("op", \%opts));
676    $TODAY = strftime("%B %e, %Y", localtime(time()));
677    $TODAY =~ s,\s+, ,g;
678    if ($opts{o} || $opts{p}) {
679	foreach my $fn (@ARGV) {
680	    readproto($fn);
681	}
682	gensummary('openpam')
683	    if ($opts{o});
684	gensummary('pam')
685	    if ($opts{p});
686    } else {
687	foreach my $fn (@ARGV) {
688	    my $func = parse_source($fn);
689	    gendoc($func)
690		if (defined($func));
691	}
692    }
693    exit(0);
694}
695