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