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