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

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

Teach gendoc to parse a simple syntax for inline documentation.

Sponsored by: DARPA, NAI Labs

File size: 11.2 KB
Line 
1#!/usr/bin/perl -w
2#-
3# Copyright (c) 2002 Networks Associates Technologies, Inc.
4# All rights reserved.
5#
6# This software was developed for the FreeBSD Project by ThinkSec AS and
7# NAI Labs, the Security Research Division of Network Associates, Inc.
8# under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
9# DARPA CHATS research program.
10#
11# Redistribution and use in source and binary forms, with or without
12# modification, are permitted provided that the following conditions
13# are met:
14# 1. Redistributions of source code must retain the above copyright
15#    notice, this list of conditions and the following disclaimer.
16# 2. Redistributions in binary form must reproduce the above copyright
17#    notice, this list of conditions and the following disclaimer in the
18#    documentation and/or other materials provided with the distribution.
19# 3. The name of the author may not be used to endorse or promote
20#    products derived from this software without specific prior written
21#    permission.
22#
23# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
24# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
27# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33# SUCH DAMAGE.
34#
35# $Id$
36#
37
38use strict;
39use Fcntl;
40use POSIX qw(strftime);
41use vars qw($COPYRIGHT $TODAY %FUNCTIONS %PAMERR);
42
43%PAMERR = (
44    PAM_SUCCESS                 => "Success",
45    PAM_OPEN_ERR                => "Failed to load module",
46    PAM_SYMBOL_ERR              => "Invalid symbol",
47    PAM_SERVICE_ERR             => "Error in service module",
48    PAM_SYSTEM_ERR              => "System error",
49    PAM_BUF_ERR                 => "Memory buffer error",
50    PAM_CONV_ERR                => "Conversation failure",
51    PAM_PERM_DENIED             => "Permission denied",
52    PAM_MAXTRIES                => "Maximum number of tries exceeded",
53    PAM_AUTH_ERR                => "Authentication error",
54    PAM_NEW_AUTHTOK_REQD        => "New authentication token required",
55    PAM_CRED_INSUFFICIENT       => "Insufficient credentials",
56    PAM_AUTHINFO_UNAVAIL        => "Authentication information is unavailable",
57    PAM_USER_UNKNOWN            => "Unknown user",
58    PAM_CRED_UNAVAIL            => "Failed to retrieve user credentials",
59    PAM_CRED_EXPIRED            => "User credentials have expired",
60    PAM_CRED_ERR                => "Failed to set user credentials",
61    PAM_ACCT_EXPIRED            => "User accound has expired",
62    PAM_AUTHTOK_EXPIRED         => "Password has expired",
63    PAM_SESSION_ERR             => "Session failure",
64    PAM_AUTHTOK_ERR             => "Authentication token failure",
65    PAM_AUTHTOK_RECOVERY_ERR    => "Failed to recover old authentication token",
66    PAM_AUTHTOK_LOCK_BUSY       => "Authentication token lock busy",
67    PAM_AUTHTOK_DISABLE_AGING   => "Authentication token aging disabled",
68    PAM_NO_MODULE_DATA          => "Module data not found",
69    PAM_IGNORE                  => "Ignore this module",
70    PAM_ABORT                   => "General failure",
71    PAM_TRY_AGAIN               => "Try again",
72    PAM_MODULE_UNKNOWN          => "Unknown module type",
73    PAM_DOMAIN_UNKNOWN          => "Unknown authentication domain",
74);
75
76sub parse_source($) {
77    my $fn = shift;
78
79    local *FILE;
80    my $source;
81    my $func;
82    my $descr;
83    my $type;
84    my $args;
85    my $argnames;
86    my $man;
87    my $inlist;
88    my $inliteral;
89    my %xref;
90    my @errors;
91
92    if ($fn !~ m,\.c$,) {
93        warn("$fn: not C source, ignoring\n");
94        return;
95    }
96
97    sysopen(FILE, $fn, O_RDONLY)
98        or die("$fn: open(): $!\n");
99    $source = join('', <FILE>);
100    close(FILE);
101
102    return if ($source =~ m/^ \* NOPARSE\s*$/m);
103
104    if (!defined($COPYRIGHT) && $source =~ m,^(/\*-\n.*?)\s*\*/,s) {
105        $COPYRIGHT = $1;
106        $COPYRIGHT =~ s,^.\*,.\\\",gm;
107        $COPYRIGHT =~ s,(\$Id).*?\$,$1\$,;
108        $COPYRIGHT .= "\n.\\\"";
109    }
110    $func = $fn;
111    $func =~ s,^(?:.*/)?([^/]+)\.c$,$1,;
112    if ($source !~ m,\n \* ([\S ]+)\n \*/\n\n([\S ]+)\n$func\((.*?)\)\n\{,s) {
113        warn("$fn: can't find $func\n");
114        return;
115    }
116    ($descr, $type, $args) = ($1, $2, $3);
117    $descr =~ s,^([A-Z][a-z]),lc($1),e;
118    $descr =~ s,[\.\s]*$,,;
119    while ($args =~ s/^((?:[^\(]|\([^\)]*\))*),\s*/$1\" \"/g) {
120        # nothing
121    }
122    $args =~ s/,\s+/, /gs;
123    $args = "\"$args\"";
124
125    %xref = (
126        "pam 3" => 1
127    );
128
129    if ($type eq "int") {
130        foreach (split("\n", $source)) {
131            next unless (m/^ \*\s+(!?PAM_[A-Z_]+|=[a-z_]+)\s*$/);
132            push(@errors, $1);
133        }
134        $xref{"pam_strerror 3"} = 1;
135    }
136
137    $argnames = $args;
138    $argnames =~ s/\"[^\"]+\*?\b(\w+)\"/\"$1\"/g;
139    $argnames =~ s/([\|\[\]\(\)\.\*\+\?])/\\$1/g;
140    $argnames =~ s/\" \"/|/g;
141    $argnames =~ s/^\"(.*)\"$/($1)/;
142    foreach (split("\n", $source)) {
143        s/\s*$//;
144        if (!defined($man)) {
145            if (m/^\/\*\*$/) {
146                $man = "";
147            }
148            next;
149        }
150        last if (m/^ \*\/$/);
151        s/^ \* ?//;
152        s/\\(.)/$1/gs;
153        if (m/^$/) {
154            if ($man ne "" && $man !~ m/\.Pp\n$/s) {
155                if ($inliteral) {
156                    $man .= "\0\n";
157                } elsif ($inlist) {
158                    $man .= ".El\n";
159                    $inlist = 0;
160                } else {
161                    $man .= ".Pp\n";
162                }
163            }
164            next;
165        }
166        if (m/^>(\w+)(?:\s+(\d))?$/) {
167            ++$xref{$2 ? "$1 $2" : "$1 3"};
168            next;
169        }
170        if (s/^\s+(=?\w+):\s*/.It $1/) {
171            if ($inliteral) {
172                $man .= ".Ed\n";
173                $inliteral = 0;
174            }
175            if (!$inlist) {
176                $man =~ s/\.Pp\n$//s;
177                $man .= ".Bl -tag -width 18n\n";
178                $inlist = 1;
179            }
180            s/^\.It =([A-Z][A-Z_]+)$/.It Dv $1/gs;
181            $man .= "$_\n";
182            next;
183        } elsif ($inlist && m/^\S/) {
184            $man .= ".El\n";
185            $inlist = 0;
186        } elsif ($inliteral && m/^\S/) {
187            $man .= ".Ed\n";
188            $inlist = 0;
189        } elsif ($inliteral) {
190            $man .= "$_\n";
191            next;
192        } elsif ($inlist) {
193            s/^\s+//;
194        } elsif (m/^\s+/) {
195            $man .= ".Bd -literal\n";
196            $inliteral = 1;
197            $man .= "$_\n";
198            next;
199        }
200        s/\s*=$func\b\s*/\n.Nm\n/gs;
201        s/\s*=$argnames\b\s*/\n.Va $1\n/gs;
202        s/\s*=(struct \w+(?: \*)?)\b\s*/\n.Vt $1\n/gs;
203        if (s/\s*=([a-z_]+)\b\s*/\n.Xr $1 3\n/gs) {
204            ++$xref{"$1 3"};
205        }
206        s/\s*\"(?=\w)/\n.Do\n/gs;
207        s/\"(?!\w)\s*/\n.Dc\n/gs;
208        s/\s*=([A-Z][A-Z_]+)\b\s*(?![\.,:;])/\n.Dv $1\n/gs;
209        s/\s*=([A-Z][A-Z_]+)\b([\.,:;]+)\s*/\n.Dv $1 $2\n/gs;
210        s/\s*{([A-Z][a-z] .*?)}\s*/\n.$1\n/gs;
211        $man .= "$_\n";
212    }
213    if (defined($man)) {
214        $man =~ s/(\n\.[A-Z][a-z] [\w ]+)\n([\.,:;-]\S*)\s*/$1 $2\n/gs;
215        $man =~ s/\s*$/\n/gm;
216        $man =~ s/\n+/\n/gs;
217        $man =~ s/\0//gs;
218        chomp($man);
219    } else {
220        $man = "No description available.";
221    }
222
223    $FUNCTIONS{$func} = {
224        'name'          => $func,
225        'descr'         => $descr,
226        'type'          => $type,
227        'args'          => $args,
228        'man'           => $man,
229        'xref'          => \%xref,
230        'errors'        => \@errors,
231    };
232    if ($source =~ m/^ \* NODOC\s*$/m) {
233        $FUNCTIONS{$func}->{'nodoc'} = 1;
234        $FUNCTIONS{$func}->{'nolist'} = 1;
235    }
236    if ($source =~ m/^ \* NOLIST\s*$/m) {
237        $FUNCTIONS{$func}->{'nolist'} = 1;
238    }
239}
240
241sub expand_errors($);
242sub expand_errors($) {
243    my $func = shift;           # Ref to function hash
244
245    my %errors;
246
247    if (defined($func->{'recursed'})) {
248        warn("$func->{'name'}(): loop in error spec\n");
249        return qw();
250    }
251    $func->{'recursed'} = 1;
252
253    foreach (@{$func->{'errors'}}) {
254        if (m/^(PAM_[A-Z_]+)$/) {
255            if (!defined($PAMERR{$1})) {
256                warn("$func->{'name'}(): unrecognized error: $1\n");
257                next;
258            }
259            $errors{$1} = 1;
260        } elsif (m/^!(PAM_[A-Z_]+)$/) {
261            # treat negations separately
262        } elsif (m/^=([a-z_]+)$/) {
263            if (!defined($FUNCTIONS{$1})) {
264                warn("$func->{'name'}(): reference to unknown $1()\n");
265                next;
266            }
267            foreach (expand_errors($FUNCTIONS{$1})) {
268                $errors{$_} = 1;
269            }
270        } else {
271            warn("$func->{'name'}(): invalid error specification: $_\n");
272        }
273    }
274    foreach (@{$func->{'errors'}}) {
275        if (m/^!(PAM_[A-Z_]+)$/) {
276            delete($errors{$1});
277        }
278    }
279    delete($func->{'recursed'});
280    return (sort(keys(%errors)));
281}
282
283sub gendoc($) {
284    my $func = shift;           # Ref to function hash
285
286    local *FILE;
287    my $mdoc;
288    my $fn;
289
290    return if defined($func->{'nodoc'});
291
292    $mdoc = "$COPYRIGHT
293.Dd $TODAY
294.Dt " . uc($func->{'name'}) . " 3
295.Os
296.Sh NAME
297.Nm $func->{'name'}
298.Nd $func->{'descr'}
299.Sh LIBRARY
300.Lb libpam
301.Sh SYNOPSIS
302.In security/pam_appl.h
303";
304    if ($func->{'name'} =~ m/_sm_/) {
305        $mdoc .= ".In security/pam_modules.h\n"
306    }
307    $mdoc .= ".Ft $func->{'type'}
308.Fn $func->{'name'} $func->{'args'}
309.Sh DESCRIPTION
310$func->{'man'}
311";
312    if ($func->{'type'} eq "int") {
313        $mdoc .= ".Sh RETURN VALUES
314The
315.Nm
316function returns one of the following values:
317.Bl -tag -width 18n
318";
319        my @errors = expand_errors($func);
320        warn("$func->{'name'}(): no error specification\n")
321            unless(@errors);
322        foreach (@errors) {
323            $mdoc .= ".It Bq Er $_\n$PAMERR{$_}.\n";
324        }
325        $mdoc .= ".El\n";
326    } else {
327        if ($func->{'type'} =~ m/\*$/) {
328            $mdoc .= ".Sh RETURN VALUES
329The
330.Nm
331function returns
332.Dv NULL
333on failure.
334";
335        }
336    }
337    $mdoc .= ".Sh SEE ALSO\n";
338    my @xref = sort(keys(%{$func->{'xref'}}));
339    while (@xref) {
340        $mdoc .= ".Xr " . shift(@xref) . (@xref ? " ,\n" : "\n");
341    }
342    $mdoc .= ".Sh STANDARDS
343.Rs
344.%T \"X/Open Single Sign-On Service (XSSO) - Pluggable Authentication Modules\"
345.%D \"June 1997\"
346.Re
347.Sh AUTHORS
348The
349.Nm
350function and this manual page were developed for the FreeBSD Project
351by ThinkSec AS and NAI Labs, the Security Research Division of Network
352Associates, Inc.  under DARPA/SPAWAR contract N66001-01-C-8035
353.Pq Dq CBOSS ,
354as part of the DARPA CHATS research program.
355";
356
357    $fn = "$func->{'name'}.3";
358    sysopen(FILE, $fn, O_RDWR|O_CREAT|O_TRUNC)
359        or die("$fn: open(): $!\n");
360    print(FILE $mdoc);
361    close(FILE);
362}
363
364sub gensummary() {
365
366    my $func;
367
368    print "$COPYRIGHT
369.Dd $TODAY
370.Dt PAM 3
371.Os
372.Sh NAME
373";
374    my @funcs = sort(keys(%FUNCTIONS));
375    while ($func = shift(@funcs)) {
376        next if (defined($FUNCTIONS{$func}->{'nolist'}));
377        print ".Nm $func". (@funcs ? " ,\n" : "\n");
378    }
379    print ".Nd Pluggable Authentication Modules Library
380.Sh LIBRARY
381.Lb libpam
382.Sh SYNOPSIS
383.In security/pam_appl.h
384";
385    foreach $func (sort(keys(%FUNCTIONS))) {
386        next if (defined($FUNCTIONS{$func}->{'nolist'}));
387        print ".Ft $FUNCTIONS{$func}->{'type'}\n";
388        print ".Fn $func $FUNCTIONS{$func}->{'args'}\n";
389    }
390    print ".Sh DESCRIPTION
391.Sh RETURN VALUES
392The following return codes are defined in the
393.In security/pam_constants.h
394header:
395.Bl -tag -width 18n
396";
397    foreach (sort(keys(%PAMERR))) {
398        print ".It Bq Er $_\n$PAMERR{$_}.\n";
399    }
400    print ".El
401.Sh SEE ALSO
402";
403    foreach $func (sort(keys(%FUNCTIONS))) {
404        next if (defined($FUNCTIONS{$func}->{'nolist'}));
405        print ".Xr $func 3 ,\n";
406    }
407    print ".Xr pam.conf 5
408.Sh STANDARDS
409.Rs
410.%T \"X/Open Single Sign-On Service (XSSO) - Pluggable Authentication Modules\"
411.%D \"June 1997\"
412.Re
413.Sh AUTHORS
414The OpenPAM library and this manual page were developed for the
415FreeBSD Project by ThinkSec AS and NAI Labs, the Security Research
416Division of Network Associates, Inc.  under DARPA/SPAWAR contract
417N66001-01-C-8035
418.Pq Dq CBOSS ,
419as part of the DARPA CHATS research program.
420"
421}
422
423MAIN:{
424    $TODAY = strftime("%B %e, %Y", localtime(time()));
425    $TODAY =~ s,\s+, ,g;
426    foreach my $fn (@ARGV) {
427        parse_source($fn);
428    }
429    foreach my $func (values(%FUNCTIONS)) {
430        gendoc($func);
431    }
432    gensummary();
433}
Note: See TracBrowser for help on using the repository browser.