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 465 2011-11-02 20:34:26Z des $ 37# 38 39use strict; 40use locale; 41use Fcntl; 42use Getopt::Std; 43use POSIX qw(locale_h strftime); 44use 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 Smørgrav 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 85Security Research Division of Network Associates, Inc.\\& under 86DARPA/SPAWAR contract N66001-01-C-8035 87.Pq Dq CBOSS , 88as 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 125sub 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 345sub expand_errors($); 346sub 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 395sub dictionary_order($$) { 396 my ($a, $b) = @_; 397 398 $a =~ s/[^[:alpha:]]//g; 399 $b =~ s/[^[:alpha:]]//g; 400 $a cmp $b; 401} 402 403sub 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 420sub 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 455The 456.Nm 457function 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 470The 471.Nm 472function returns 473.Dv NULL 474on 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 483function 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 493The 494.Nm 495function and this manual page were developed for the 496.Fx 497Project 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 507sub 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 532sub 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 580The 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 604The OpenPAM library and this manual page were developed for the 605.Fx 606Project by ThinkSec AS and Network Associates Laboratories, the 607Security Research Division of Network Associates, Inc.\\& under 608DARPA/SPAWAR contract N66001-01-C-8035 609.Pq Dq CBOSS , 610as part of the DARPA CHATS research program. 611"; 612 close(FILE); 613} 614 615sub usage() { 616 617 print(STDERR "usage: gendoc [-op] source [...]\n"); 618 exit(1); 619} 620 621MAIN:{ 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