source: openpam/trunk/misc/gendoc.pl @ 929

Last change on this file since 929 was 929, checked in by Dag-Erling Smørgrav, 4 years ago

Add the new error codes and rewrite the error code handling to support
negated entries, e.g. in cases a() calls b() and b() can return code X
in certain situations, but a() ensures that this never happens.

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