gendoc.pl revision 1.1 1 #!/usr/bin/perl -w
2 #-
3 # Copyright (c) 2002-2003 Networks Associates Technology, Inc.
4 # Copyright (c) 2004-2011 Dag-Erling Smrgrav
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 465 2011-11-02 20:34:26Z des
37 #
38
39 use strict;
40 use locale;
41 use Fcntl;
42 use Getopt::Std;
43 use POSIX qw(locale_h strftime);
44 use vars qw($COPYRIGHT %AUTHORS $TODAY %FUNCTIONS %PAMERR);
45
46 $COPYRIGHT = ".\\\"-
47 .\\\" Copyright (c) 2001-2003 Networks Associates Technology, Inc.
48 .\\\" Copyright (c) 2004-2011 Dag-Erling Smrgrav
49 .\\\" All rights reserved.
50 .\\\"
51 .\\\" This software was developed for the FreeBSD Project by ThinkSec AS and
52 .\\\" Network Associates Laboratories, the Security Research Division of
53 .\\\" Network Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035
54 .\\\" (\"CBOSS\"), as part of the DARPA CHATS research program.
55 .\\\"
56 .\\\" Redistribution and use in source and binary forms, with or without
57 .\\\" modification, are permitted provided that the following conditions
58 .\\\" are met:
59 .\\\" 1. Redistributions of source code must retain the above copyright
60 .\\\" notice, this list of conditions and the following disclaimer.
61 .\\\" 2. Redistributions in binary form must reproduce the above copyright
62 .\\\" notice, this list of conditions and the following disclaimer in the
63 .\\\" documentation and/or other materials provided with the distribution.
64 .\\\" 3. The name of the author may not be used to endorse or promote
65 .\\\" products derived from this software without specific prior written
66 .\\\" permission.
67 .\\\"
68 .\\\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
69 .\\\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
70 .\\\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
71 .\\\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
72 .\\\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
73 .\\\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
74 .\\\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
75 .\\\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
76 .\\\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
77 .\\\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
78 .\\\" SUCH DAMAGE.
79 .\\\"
80 .\\\" \$" . "Id" . "\$
81 .\\\"";
82
83 %AUTHORS = (
84 THINKSEC => "ThinkSec AS and Network Associates Laboratories, the
85 Security Research Division of Network Associates, Inc.\\& under
86 DARPA/SPAWAR contract N66001-01-C-8035
87 .Pq Dq CBOSS ,
88 as part of the DARPA CHATS research program.",
89 DES => ".An Dag-Erling Sm\\(/orgrav Aq des\@FreeBSD.org .",
90 );
91
92 %PAMERR = (
93 PAM_SUCCESS => "Success",
94 PAM_OPEN_ERR => "Failed to load module",
95 PAM_SYMBOL_ERR => "Invalid symbol",
96 PAM_SERVICE_ERR => "Error in service module",
97 PAM_SYSTEM_ERR => "System error",
98 PAM_BUF_ERR => "Memory buffer error",
99 PAM_CONV_ERR => "Conversation failure",
100 PAM_PERM_DENIED => "Permission denied",
101 PAM_MAXTRIES => "Maximum number of tries exceeded",
102 PAM_AUTH_ERR => "Authentication error",
103 PAM_NEW_AUTHTOK_REQD => "New authentication token required",
104 PAM_CRED_INSUFFICIENT => "Insufficient credentials",
105 PAM_AUTHINFO_UNAVAIL => "Authentication information is unavailable",
106 PAM_USER_UNKNOWN => "Unknown user",
107 PAM_CRED_UNAVAIL => "Failed to retrieve user credentials",
108 PAM_CRED_EXPIRED => "User credentials have expired",
109 PAM_CRED_ERR => "Failed to set user credentials",
110 PAM_ACCT_EXPIRED => "User account has expired",
111 PAM_AUTHTOK_EXPIRED => "Password has expired",
112 PAM_SESSION_ERR => "Session failure",
113 PAM_AUTHTOK_ERR => "Authentication token failure",
114 PAM_AUTHTOK_RECOVERY_ERR => "Failed to recover old authentication token",
115 PAM_AUTHTOK_LOCK_BUSY => "Authentication token lock busy",
116 PAM_AUTHTOK_DISABLE_AGING => "Authentication token aging disabled",
117 PAM_NO_MODULE_DATA => "Module data not found",
118 PAM_IGNORE => "Ignore this module",
119 PAM_ABORT => "General failure",
120 PAM_TRY_AGAIN => "Try again",
121 PAM_MODULE_UNKNOWN => "Unknown module type",
122 PAM_DOMAIN_UNKNOWN => "Unknown authentication domain",
123 );
124
125 sub parse_source($) {
126 my $fn = shift;
127
128 local *FILE;
129 my $source;
130 my $func;
131 my $descr;
132 my $type;
133 my $args;
134 my $argnames;
135 my $man;
136 my $inlist;
137 my $intaglist;
138 my $inliteral;
139 my %xref;
140 my @errors;
141 my $author;
142
143 if ($fn !~ m,\.c$,) {
144 warn("$fn: not C source, ignoring\n");
145 return undef;
146 }
147
148 open(FILE, "<", "$fn")
149 or die("$fn: open(): $!\n");
150 $source = join('', <FILE>);
151 close(FILE);
152
153 return undef
154 if ($source =~ m/^ \* NOPARSE\s*$/m);
155
156 $author = 'THINKSEC';
157 if ($source =~ s/^ \* AUTHOR\s+(.*?)\s*$//m) {
158 $author = $1;
159 }
160
161 $func = $fn;
162 $func =~ s,^(?:.*/)?([^/]+)\.c$,$1,;
163 if ($source !~ m,\n \* ([\S ]+)\n \*/\n\n([\S ]+)\n$func\((.*?)\)\n\{,s) {
164 warn("$fn: can't find $func\n");
165 return undef;
166 }
167 ($descr, $type, $args) = ($1, $2, $3);
168 $descr =~ s,^([A-Z][a-z]),lc($1),e;
169 $descr =~ s,[\.\s]*$,,;
170 while ($args =~ s/^((?:[^\(]|\([^\)]*\))*),\s*/$1\" \"/g) {
171 # nothing
172 }
173 $args =~ s/,\s+/, /gs;
174 $args = "\"$args\"";
175
176 %xref = (
177 3 => { 'pam' => 1 },
178 );
179
180 if ($type eq "int") {
181 foreach (split("\n", $source)) {
182 next unless (m/^ \*\s+(!?PAM_[A-Z_]+|=[a-z_]+)\s*$/);
183 push(@errors, $1);
184 }
185 ++$xref{3}->{'pam_strerror'};
186 }
187
188 $argnames = $args;
189 # extract names of regular arguments
190 $argnames =~ s/\"[^\"]+\*?\b(\w+)\"/\"$1\"/g;
191 # extract names of function pointer arguments
192 $argnames =~ s/\"([\w\s\*]+)\(\*?(\w+)\)\([^\)]+\)\"/\"$2\"/g;
193 # escape metacharacters (there shouldn't be any, but...)
194 $argnames =~ s/([\|\[\]\(\)\.\*\+\?])/\\$1/g;
195 # separate argument names with |
196 $argnames =~ s/\" \"/|/g;
197 # and surround with ()
198 $argnames =~ s/^\"(.*)\"$/($1)/;
199 # $argnames is now a regexp that matches argument names
200 $inliteral = $inlist = $intaglist = 0;
201 foreach (split("\n", $source)) {
202 s/\s*$//;
203 if (!defined($man)) {
204 if (m/^\/\*\*$/) {
205 $man = "";
206 }
207 next;
208 }
209 last if (m/^ \*\/$/);
210 s/^ \* ?//;
211 s/\\(.)/$1/gs;
212 if (m/^$/) {
213 # paragraph separator
214 if ($man ne "" && $man !~ m/\.Pp\n$/s) {
215 if ($inliteral) {
216 $man .= "\0\n";
217 } elsif ($inlist || $intaglist) {
218 $man .= ".El\n.Pp\n";
219 $inlist = $intaglist = 0;
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/^\s+-\s+//) {
233 # item in bullet list
234 if ($inliteral) {
235 $man .= ".Ed\n";
236 $inliteral = 0;
237 }
238 if ($intaglist) {
239 $man .= ".El\n.Pp\n";
240 $intaglist = 0;
241 }
242 if (!$inlist) {
243 $man =~ s/\.Pp\n$//s;
244 $man .= ".Bl -bullet\n";
245 $inlist = 1;
246 }
247 $man .= ".It\n";
248 # fall through
249 } elsif (s/^\s+(\S+):\s*/.It $1/) {
250 # item in tag list
251 if ($inliteral) {
252 $man .= ".Ed\n";
253 $inliteral = 0;
254 }
255 if ($inlist) {
256 $man .= ".El\n.Pp\n";
257 $inlist = 0;
258 }
259 if (!$intaglist) {
260 $man =~ s/\.Pp\n$//s;
261 $man .= ".Bl -tag -width 18n\n";
262 $intaglist = 1;
263 }
264 s/^\.It =([A-Z][A-Z_]+)$/.It Dv $1/gs;
265 $man .= "$_\n";
266 next;
267 } elsif (($inlist || $intaglist) && m/^\S/) {
268 # regular text after list
269 $man .= ".El\n.Pp\n";
270 $inlist = $intaglist = 0;
271 } elsif ($inliteral && m/^\S/) {
272 # regular text after literal section
273 $man .= ".Ed\n";
274 $inliteral = 0;
275 } elsif ($inliteral) {
276 # additional text within literal section
277 $man .= "$_\n";
278 next;
279 } elsif ($inlist || $intaglist) {
280 # additional text within list
281 s/^\s+//;
282 } elsif (m/^\s+/) {
283 # new literal section
284 $man .= ".Bd -literal\n";
285 $inliteral = 1;
286 $man .= "$_\n";
287 next;
288 }
289 s/\s*=$func\b\s*/\n.Nm\n/gs;
290 s/\s*=$argnames\b\s*/\n.Fa $1\n/gs;
291 s/\s*=(struct \w+(?: \*)?)\b\s*/\n.Vt $1\n/gs;
292 s/\s*:([a-z_]+)\b\s*/\n.Va $1\n/gs;
293 s/\s*;([a-z_]+)\b\s*/\n.Dv $1\n/gs;
294 while (s/\s*=([a-z_]+)\b\s*/\n.Xr $1 3\n/s) {
295 ++$xref{3}->{$1};
296 }
297 s/\s*\"(?=\w)/\n.Do\n/gs;
298 s/\"(?!\w)\s*/\n.Dc\n/gs;
299 s/\s*=([A-Z][A-Z_]+)\b\s*(?![\.,:;])/\n.Dv $1\n/gs;
300 s/\s*=([A-Z][A-Z_]+)\b([\.,:;]+)\s*/\n.Dv $1 $2\n/gs;
301 s/\s*{([A-Z][a-z] .*?)}\s*/\n.$1\n/gs;
302 $man .= "$_\n";
303 }
304 if (defined($man)) {
305 if ($inlist || $intaglist) {
306 $man .= ".El\n";
307 $inlist = $intaglist = 0;
308 }
309 if ($inliteral) {
310 $man .= ".Ed\n";
311 $inliteral = 0;
312 }
313 $man =~ s/\%/\\&\%/gs;
314 $man =~ s/(\n\.[A-Z][a-z] [\w ]+)\n([\.,:;-]\S*)\s*/$1 $2\n/gs;
315 $man =~ s/\s*$/\n/gm;
316 $man =~ s/\n+/\n/gs;
317 $man =~ s/\0//gs;
318 $man =~ s/\n\n\./\n\./gs;
319 chomp($man);
320 } else {
321 $man = "No description available.";
322 }
323
324 $FUNCTIONS{$func} = {
325 'source' => $fn,
326 'name' => $func,
327 'descr' => $descr,
328 'type' => $type,
329 'args' => $args,
330 'man' => $man,
331 'xref' => \%xref,
332 'errors' => \@errors,
333 'author' => $author,
334 };
335 if ($source =~ m/^ \* NODOC\s*$/m) {
336 $FUNCTIONS{$func}->{'nodoc'} = 1;
337 }
338 if ($source !~ m/^ \* XSSO \d/m) {
339 $FUNCTIONS{$func}->{'openpam'} = 1;
340 }
341 expand_errors($FUNCTIONS{$func});
342 return $FUNCTIONS{$func};
343 }
344
345 sub expand_errors($);
346 sub expand_errors($) {
347 my $func = shift; # Ref to function hash
348
349 my %errors;
350 my $ref;
351 my $fn;
352
353 if (defined($func->{'recursed'})) {
354 warn("$func->{'name'}(): loop in error spec\n");
355 return qw();
356 }
357 $func->{'recursed'} = 1;
358
359 foreach (@{$func->{'errors'}}) {
360 if (m/^(PAM_[A-Z_]+)$/) {
361 if (!defined($PAMERR{$1})) {
362 warn("$func->{'name'}(): unrecognized error: $1\n");
363 next;
364 }
365 $errors{$1} = 1;
366 } elsif (m/^!(PAM_[A-Z_]+)$/) {
367 # treat negations separately
368 } elsif (m/^=([a-z_]+)$/) {
369 $ref = $1;
370 if (!defined($FUNCTIONS{$ref})) {
371 $fn = $func->{'source'};
372 $fn =~ s/$func->{'name'}/$ref/;
373 parse_source($fn);
374 }
375 if (!defined($FUNCTIONS{$ref})) {
376 warn("$func->{'name'}(): reference to unknown $ref()\n");
377 next;
378 }
379 foreach (@{$FUNCTIONS{$ref}->{'errors'}}) {
380 $errors{$_} = 1;
381 }
382 } else {
383 warn("$func->{'name'}(): invalid error specification: $_\n");
384 }
385 }
386 foreach (@{$func->{'errors'}}) {
387 if (m/^!(PAM_[A-Z_]+)$/) {
388 delete($errors{$1});
389 }
390 }
391 delete($func->{'recursed'});
392 $func->{'errors'} = [ sort(keys(%errors)) ];
393 }
394
395 sub dictionary_order($$) {
396 my ($a, $b) = @_;
397
398 $a =~ s/[^[:alpha:]]//g;
399 $b =~ s/[^[:alpha:]]//g;
400 $a cmp $b;
401 }
402
403 sub genxref($) {
404 my $xref = shift; # References
405
406 my $mdoc = '';
407 my @refs = ();
408 foreach my $sect (sort(keys(%{$xref}))) {
409 foreach my $page (sort(dictionary_order keys(%{$xref->{$sect}}))) {
410 push(@refs, "$page $sect");
411 }
412 }
413 while ($_ = shift(@refs)) {
414 $mdoc .= ".Xr $_" .
415 (@refs ? " ,\n" : "\n");
416 }
417 return $mdoc;
418 }
419
420 sub gendoc($) {
421 my $func = shift; # Ref to function hash
422
423 local *FILE;
424 my $mdoc;
425 my $fn;
426
427 return if defined($func->{'nodoc'});
428
429 $mdoc = "$COPYRIGHT
430 .Dd $TODAY
431 .Dt " . uc($func->{'name'}) . " 3
432 .Os
433 .Sh NAME
434 .Nm $func->{'name'}
435 .Nd $func->{'descr'}
436 .Sh LIBRARY
437 .Lb libpam
438 .Sh SYNOPSIS
439 .In sys/types.h
440 .In security/pam_appl.h
441 ";
442 if ($func->{'name'} =~ m/_sm_/) {
443 $mdoc .= ".In security/pam_modules.h\n"
444 }
445 if ($func->{'name'} =~ m/openpam/) {
446 $mdoc .= ".In security/openpam.h\n"
447 }
448 $mdoc .= ".Ft \"$func->{'type'}\"
449 .Fn $func->{'name'} $func->{'args'}
450 .Sh DESCRIPTION
451 $func->{'man'}
452 ";
453 if ($func->{'type'} eq "int") {
454 $mdoc .= ".Sh RETURN VALUES
455 The
456 .Nm
457 function returns one of the following values:
458 .Bl -tag -width 18n
459 ";
460 my @errors = @{$func->{'errors'}};
461 warn("$func->{'name'}(): no error specification\n")
462 unless(@errors);
463 foreach (@errors) {
464 $mdoc .= ".It Bq Er $_\n$PAMERR{$_}.\n";
465 }
466 $mdoc .= ".El\n";
467 } else {
468 if ($func->{'type'} =~ m/\*$/) {
469 $mdoc .= ".Sh RETURN VALUES
470 The
471 .Nm
472 function returns
473 .Dv NULL
474 on failure.
475 ";
476 }
477 }
478 $mdoc .= ".Sh SEE ALSO\n" . genxref($func->{'xref'});
479 $mdoc .= ".Sh STANDARDS\n";
480 if ($func->{'openpam'}) {
481 $mdoc .= "The
482 .Nm
483 function is an OpenPAM extension.
484 ";
485 } else {
486 $mdoc .= ".Rs
487 .%T \"X/Open Single Sign-On Service (XSSO) - Pluggable Authentication Modules\"
488 .%D \"June 1997\"
489 .Re
490 ";
491 }
492 $mdoc .= ".Sh AUTHORS
493 The
494 .Nm
495 function and this manual page were developed for the
496 .Fx
497 Project by\n" . $AUTHORS{$func->{'author'} // 'THINKSEC_DARPA'} . "\n";
498 $fn = "$func->{'name'}.3";
499 if (open(FILE, ">", $fn)) {
500 print(FILE $mdoc);
501 close(FILE);
502 } else {
503 warn("$fn: open(): $!\n");
504 }
505 }
506
507 sub readproto($) {
508 my $fn = shift; # File name
509
510 local *FILE;
511 my %func;
512
513 open(FILE, "<", "$fn")
514 or die("$fn: open(): $!\n");
515 while (<FILE>) {
516 if (m/^\.Nm ((?:open)?pam_.*?)\s*$/) {
517 $func{'Nm'} = $func{'Nm'} || $1;
518 } elsif (m/^\.Ft (\S.*?)\s*$/) {
519 $func{'Ft'} = $func{'Ft'} || $1;
520 } elsif (m/^\.Fn (\S.*?)\s*$/) {
521 $func{'Fn'} = $func{'Fn'} || $1;
522 }
523 }
524 close(FILE);
525 if ($func{'Nm'}) {
526 $FUNCTIONS{$func{'Nm'}} = \%func;
527 } else {
528 warn("No function found\n");
529 }
530 }
531
532 sub gensummary($) {
533 my $page = shift; # Which page to produce
534
535 local *FILE;
536 my $upage;
537 my $func;
538 my %xref;
539
540 open(FILE, ">", "$page.3")
541 or die("$page.3: $!\n");
542
543 $page =~ m/(\w+)$/;
544 $upage = uc($1);
545 print FILE "$COPYRIGHT
546 .Dd $TODAY
547 .Dt $upage 3
548 .Os
549 .Sh NAME
550 ";
551 my @funcs = sort(keys(%FUNCTIONS));
552 while ($func = shift(@funcs)) {
553 print FILE ".Nm $FUNCTIONS{$func}->{'Nm'}";
554 print FILE " ,"
555 if (@funcs);
556 print FILE "\n";
557 }
558 print FILE ".Nd Pluggable Authentication Modules Library
559 .Sh LIBRARY
560 .Lb libpam
561 .Sh SYNOPSIS\n";
562 if ($page eq 'pam') {
563 print FILE ".In security/pam_appl.h\n";
564 } else {
565 print FILE ".In security/openpam.h\n";
566 }
567 foreach $func (sort(keys(%FUNCTIONS))) {
568 print FILE ".Ft $FUNCTIONS{$func}->{'Ft'}\n";
569 print FILE ".Fn $FUNCTIONS{$func}->{'Fn'}\n";
570 }
571 while (<STDIN>) {
572 if (m/^\.Xr (\S+)\s*(\d)\s*$/) {
573 ++$xref{int($2)}->{$1};
574 }
575 print FILE $_;
576 }
577
578 if ($page eq 'pam') {
579 print FILE ".Sh RETURN VALUES
580 The following return codes are defined by
581 .In security/pam_constants.h :
582 .Bl -tag -width 18n
583 ";
584 foreach (sort(keys(%PAMERR))) {
585 print FILE ".It Bq Er $_\n$PAMERR{$_}.\n";
586 }
587 print FILE ".El\n";
588 }
589 print FILE ".Sh SEE ALSO
590 ";
591 if ($page eq 'pam') {
592 ++$xref{3}->{'openpam'};
593 }
594 foreach $func (keys(%FUNCTIONS)) {
595 ++$xref{3}->{$func};
596 }
597 print FILE genxref(\%xref);
598 print FILE ".Sh STANDARDS
599 .Rs
600 .%T \"X/Open Single Sign-On Service (XSSO) - Pluggable Authentication Modules\"
601 .%D \"June 1997\"
602 .Re
603 .Sh AUTHORS
604 The OpenPAM library and this manual page were developed for the
605 .Fx
606 Project by ThinkSec AS and Network Associates Laboratories, the
607 Security Research Division of Network Associates, Inc.\\& under
608 DARPA/SPAWAR contract N66001-01-C-8035
609 .Pq Dq CBOSS ,
610 as part of the DARPA CHATS research program.
611 ";
612 close(FILE);
613 }
614
615 sub usage() {
616
617 print(STDERR "usage: gendoc [-op] source [...]\n");
618 exit(1);
619 }
620
621 MAIN:{
622 my %opts;
623
624 usage()
625 unless (@ARGV && getopts("op", \%opts));
626 setlocale(LC_ALL, "en_US.UTF-8");
627 $TODAY = strftime("%B %e, %Y", localtime(time()));
628 $TODAY =~ s,\s+, ,g;
629 if ($opts{'o'} || $opts{'p'}) {
630 foreach my $fn (@ARGV) {
631 readproto($fn);
632 }
633 gensummary('openpam')
634 if ($opts{'o'});
635 gensummary('pam')
636 if ($opts{'p'});
637 } else {
638 foreach my $fn (@ARGV) {
639 my $func = parse_source($fn);
640 gendoc($func)
641 if (defined($func));
642 }
643 }
644 exit(0);
645 }
646