source: openpam/branches/oath/misc/gendoc.pl @ 911

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

merge r910: correct markup for email addresses

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 17.5 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 911 2017-01-21 12:37:15Z 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);
97
98sub 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*=((?:enum|struct|union) \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    if ($func =~ m/^oath/) {
351        $FUNCTIONS{$func}->{oath} = 1;
352    }
353    expand_errors($FUNCTIONS{$func});
354    return $FUNCTIONS{$func};
355}
356
357sub expand_errors($);
358sub expand_errors($) {
359    my $func = shift;           # Ref to function hash
360
361    my %errors;
362    my $ref;
363    my $fn;
364
365    if (defined($$func{recursed})) {
366        warn("$$func{name}(): loop in error spec\n");
367        return qw();
368    }
369    $$func{recursed} = 1;
370
371    foreach (@{$$func{errors}}) {
372        if (m/^(PAM_[A-Z_]+)$/) {
373            if (!defined($PAMERR{$1})) {
374                warn("$$func{name}(): unrecognized error: $1\n");
375                next;
376            }
377            $errors{$1} = 1;
378        } elsif (m/^!(PAM_[A-Z_]+)$/) {
379            # treat negations separately
380        } elsif (m/^=([a-z_]+)$/) {
381            $ref = $1;
382            if (!defined($FUNCTIONS{$ref})) {
383                $fn = $$func{source};
384                $fn =~ s/$$func{name}/$ref/;
385                parse_source($fn);
386            }
387            if (!defined($FUNCTIONS{$ref})) {
388                warn("$$func{name}(): reference to unknown $ref()\n");
389                next;
390            }
391            foreach (@{$FUNCTIONS{$ref}->{errors}}) {
392                $errors{$_} = 1;
393            }
394        } else {
395            warn("$$func{name}(): invalid error specification: $_\n");
396        }
397    }
398    foreach (@{$$func{errors}}) {
399        if (m/^!(PAM_[A-Z_]+)$/) {
400            delete($errors{$1});
401        }
402    }
403    delete($$func{recursed});
404    $$func{errors} = [ sort(keys(%errors)) ];
405}
406
407sub dictionary_order($$) {
408    my ($a, $b) = @_;
409
410    $a =~ s/[^[:alpha:]]//g;
411    $b =~ s/[^[:alpha:]]//g;
412    $a cmp $b;
413}
414
415sub genxref($) {
416    my $xref = shift;           # References
417
418    my $mdoc = '';
419    my @refs = ();
420    foreach my $sect (sort(keys(%{$xref}))) {
421        foreach my $page (sort(dictionary_order keys(%{$xref->{$sect}}))) {
422            push(@refs, "$page $sect");
423        }
424    }
425    while ($_ = shift(@refs)) {
426        $mdoc .= ".Xr $_" .
427            (@refs ? " ,\n" : "\n");
428    }
429    return $mdoc;
430}
431
432sub gendoc($) {
433    my $func = shift;           # Ref to function hash
434
435    local *FILE;
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    } elsif ($func =~ m/^oath_/) {
458        $mdoc .= ".Sh LIBRARY
459.Lb liboath
460";
461    }
462    $mdoc .= ".Sh SYNOPSIS
463.In sys/types.h
464";
465    if ($$func{name} =~ m/^oath/) {
466        $mdoc .= ".In stdint.h\n";
467    }
468    if ($$func{args} =~ m/\bFILE \*\b/) {
469        $mdoc .= ".In stdio.h\n";
470    }
471    if ($$func{name} =~ m/^oath/) {
472        $mdoc .= ".In security/oath.h
473";
474    }
475    if ($$func{name} =~ m/^(?:open)?pam/) {
476        $mdoc .= ".In security/pam_appl.h
477";
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 prior 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
511The
512.Fn $$func{name}
513function 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
522The
523.Fn $$func{name}
524function returns 0 on success and -1 on failure.
525";
526    } elsif ($$func{type} =~ m/\*$/) {
527        $mdoc .= ".Sh RETURN VALUES
528The
529.Fn $$func{name}
530function returns
531.Dv NULL
532on 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}
542function is an OpenPAM extension.
543";
544    } elsif ($$func{oath}) {
545        # nothing yet
546    } else {
547        $mdoc .= ".Rs
548.%T \"X/Open Single Sign-On Service (XSSO) - Pluggable Authentication Modules\"
549.%D \"June 1997\"
550.Re
551";
552    }
553    $mdoc .= ".Sh AUTHORS
554The
555.Fn $$func{name}
556function and this manual page were\n";
557    $mdoc .= $AUTHORS{$$func{author} // 'THINKSEC_DARPA'} . "\n";
558    $fn = "$$func{name}.3";
559    if (open(FILE, ">", $fn)) {
560        print(FILE $mdoc);
561        close(FILE);
562    } else {
563        warn("$fn: open(): $!\n");
564    }
565}
566
567sub readproto($) {
568    my $fn = shift;             # File name
569
570    local *FILE;
571    my %func;
572
573    open(FILE, "<", "$fn")
574        or die("$fn: open(): $!\n");
575    while (<FILE>) {
576        if (m/^\.Nm ((?:(?:open)?pam|oath)_.*?)\s*$/) {
577            $func{Nm} = $func{Nm} || $1;
578        } elsif (m/^\.Ft (\S.*?)\s*$/) {
579            $func{Ft} = $func{Ft} || $1;
580        } elsif (m/^\.Fn (\S.*?)\s*$/) {
581            $func{Fn} = $func{Fn} || $1;
582        }
583    }
584    close(FILE);
585    if ($func{Nm}) {
586        $FUNCTIONS{$func{Nm}} = \%func;
587    } else {
588        warn("No function found\n");
589    }
590}
591
592sub gensummary($) {
593    my $page = shift;           # Which page to produce
594
595    local *FILE;
596    my $upage;
597    my $func;
598    my %xref;
599
600    open(FILE, ">", "$page.3")
601        or die("$page.3: $!\n");
602
603    $page =~ m/(\w+)$/;
604    $upage = uc($1);
605    print FILE ".\\\" Generated by gendoc.pl
606.Dd $TODAY
607.Dt $upage 3
608.Os
609.Sh NAME
610";
611    my @funcs = sort(keys(%FUNCTIONS));
612    while ($func = shift(@funcs)) {
613        print FILE ".Nm $FUNCTIONS{$func}->{Nm}";
614        print FILE " ,"
615                if (@funcs);
616        print FILE "\n";
617    }
618    print FILE ".Nd Pluggable Authentication Modules Library
619.Sh LIBRARY
620.Lb libpam
621.Sh SYNOPSIS\n";
622    if ($page eq 'pam') {
623        print FILE ".In security/pam_appl.h\n";
624    } elsif ($page eq 'oath') {
625        print FILE ".In security/oath.h\n";
626    } else {
627        print FILE ".In security/openpam.h\n";
628    }
629    foreach $func (sort(keys(%FUNCTIONS))) {
630        print FILE ".Ft $FUNCTIONS{$func}->{Ft}\n";
631        print FILE ".Fn $FUNCTIONS{$func}->{Fn}\n";
632    }
633    while (<STDIN>) {
634        if (m/^\.Xr (\S+)\s*(\d)\s*$/) {
635            ++$xref{int($2)}->{$1};
636        }
637        print FILE $_;
638    }
639
640    if ($page eq 'pam') {
641        print FILE ".Sh RETURN VALUES
642The following return codes are defined by
643.In security/pam_constants.h :
644.Bl -tag -width 18n
645";
646        foreach (sort(keys(%PAMERR))) {
647            print FILE ".It Bq Er $_\n$PAMERR{$_}.\n";
648        }
649        print FILE ".El\n";
650    }
651    print FILE ".Sh SEE ALSO
652";
653    if ($page eq 'pam') {
654        ++$xref{3}->{openpam};
655    }
656    foreach $func (keys(%FUNCTIONS)) {
657        ++$xref{3}->{$func};
658    }
659    print FILE genxref(\%xref);
660    if ($page eq 'oath') {
661        print FILE ".Sh AUTHORS
662The OATH library and this manual page were $AUTHORS{UIO}
663";
664    } else {
665        print FILE ".Sh STANDARDS
666.Rs
667.%T \"X/Open Single Sign-On Service (XSSO) - Pluggable Authentication Modules\"
668.%D \"June 1997\"
669.Re
670";
671        print FILE ".Sh AUTHORS
672The OpenPAM library and this manual page were $AUTHORS{THINKSEC}
673";
674    }
675    close(FILE);
676}
677
678sub usage() {
679
680    print(STDERR "usage: gendoc [-op] source [...]\n");
681    exit(1);
682}
683
684MAIN:{
685    my %opts;
686
687    usage()
688        unless (@ARGV && getopts("aop", \%opts));
689    $TODAY = strftime("%B %e, %Y", localtime(time()));
690    $TODAY =~ s,\s+, ,g;
691    if ($opts{a} || $opts{o} || $opts{p}) {
692        foreach my $fn (@ARGV) {
693            readproto($fn);
694        }
695        gensummary('oath')
696            if ($opts{a});
697        gensummary('openpam')
698            if ($opts{o});
699        gensummary('pam')
700            if ($opts{p});
701    } else {
702        foreach my $fn (@ARGV) {
703            my $func = parse_source($fn);
704            gendoc($func)
705                if (defined($func));
706        }
707    }
708    exit(0);
709}
Note: See TracBrowser for help on using the repository browser.