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 $$func{source} =~ m/([^\/]+)$/; 444 $mdoc = ".\\\" Generated from $1 by gendoc.pl\n"; 445 if ($$func{version}) { 446 $mdoc .= ".\\\" $$func{version}\n"; 447 } 448 $mdoc .= ".Dd $TODAY 449.Dt " . uc($$func{name}) . " 3 450.Os 451.Sh NAME 452.Nm $$func{name} 453.Nd $$func{descr} 454"; 455 if ($func =~ m/^(?:open)?pam_/) { 456 $mdoc .= ".Sh LIBRARY 457.Lb libpam 458"; 459 } 460 $mdoc .= ".Sh SYNOPSIS 461.In sys/types.h 462"; 463 if ($$func{args} =~ m/\bFILE \*\b/) { 464 $mdoc .= ".In stdio.h\n"; 465 } 466 if ($$func{name} =~ m/^(?:open)?pam/) { 467 $mdoc .= ".In security/pam_appl.h 468"; 469 } 470 if ($$func{name} =~ m/_sm_/) { 471 $mdoc .= ".In security/pam_modules.h\n"; 472 } 473 if ($$func{name} =~ m/openpam/) { 474 $mdoc .= ".In security/openpam.h\n"; 475 } 476 $mdoc .= ".Ft \"$$func{type}\" 477.Fn $$func{name} $$func{args} 478.Sh DESCRIPTION 479"; 480 if (defined($$func{deprecated})) { 481 $mdoc .= ".Bf Sy\n" . 482 "This function is deprecated and may be removed " . 483 "in a future release without further warning.\n"; 484 if ($$func{deprecated}) { 485 $mdoc .= "The\n.Fn $$func{deprecated}\nfunction " . 486 "may be used to achieve similar results.\n"; 487 } 488 $mdoc .= ".Ef\n.Pp\n"; 489 } 490 if ($$func{experimental}) { 491 $mdoc .= ".Bf Sy\n" . 492 "This function is experimental and may be modified or removed " . 493 "in a future release without prior warning.\n"; 494 $mdoc .= ".Ef\n.Pp\n"; 495 } 496 $mdoc .= "$$func{man}\n"; 497 %errors = %{$$func{errors}}; 498 if ($$func{customrv}) { 499 # leave it 500 } elsif ($$func{type} eq "int" && %errors) { 501 $mdoc .= ".Sh RETURN VALUES 502The 503.Fn $$func{name} 504function returns one of the following values: 505.Bl -tag -width 18n 506"; 507 delete($errors{PAM_SUCCESS}); 508 foreach ('PAM_SUCCESS', sort keys %errors) { 509 $mdoc .= ".It Bq Er $_\n" . 510 ($errors{$_} || $PAMERR{$_}) . 511 ".\n"; 512 } 513 $mdoc .= ".El\n"; 514 } elsif ($$func{type} eq "int") { 515 $mdoc .= ".Sh RETURN VALUES 516The 517.Fn $$func{name} 518function returns 0 on success and -1 on failure. 519"; 520 } elsif ($$func{type} =~ m/\*$/) { 521 $mdoc .= ".Sh RETURN VALUES 522The 523.Fn $$func{name} 524function returns 525.Dv NULL 526on failure. 527"; 528 } elsif ($$func{type} ne "void") { 529 warn("$$func{name}(): no error specification\n"); 530 } 531 $mdoc .= ".Sh SEE ALSO\n" . genxref($$func{xref}); 532 $mdoc .= ".Sh STANDARDS\n"; 533 if ($$func{openpam}) { 534 $mdoc .= "The 535.Fn $$func{name} 536function is an OpenPAM extension. 537"; 538 } else { 539 $mdoc .= ".Rs 540.%T \"X/Open Single Sign-On Service (XSSO) - Pluggable Authentication Modules\" 541.%D \"June 1997\" 542.Re 543"; 544 } 545 $mdoc .= ".Sh AUTHORS 546The 547.Fn $$func{name} 548function and this manual page were\n"; 549 $mdoc .= $AUTHORS{$$func{author} // 'THINKSEC_DARPA'} . "\n"; 550 $fn = "$$func{name}.3"; 551 if (open(FILE, ">", $fn)) { 552 print(FILE $mdoc); 553 close(FILE); 554 } else { 555 warn("$fn: open(): $!\n"); 556 } 557} 558 559sub readproto($) { 560 my $fn = shift; # File name 561 562 local *FILE; 563 my %func; 564 565 open(FILE, "<", "$fn") 566 or die("$fn: open(): $!\n"); 567 while (<FILE>) { 568 if (m/^\.Nm ((?:(?:open)?pam)_.*?)\s*$/) { 569 $func{Nm} = $func{Nm} || $1; 570 } elsif (m/^\.Ft (\S.*?)\s*$/) { 571 $func{Ft} = $func{Ft} || $1; 572 } elsif (m/^\.Fn (\S.*?)\s*$/) { 573 $func{Fn} = $func{Fn} || $1; 574 } 575 } 576 close(FILE); 577 if ($func{Nm}) { 578 $FUNCTIONS{$func{Nm}} = \%func; 579 } else { 580 warn("No function found\n"); 581 } 582} 583 584sub gensummary($) { 585 my $page = shift; # Which page to produce 586 587 local *FILE; 588 my $upage; 589 my $func; 590 my %xref; 591 592 open(FILE, ">", "$page.3") 593 or die("$page.3: $!\n"); 594 595 $page =~ m/(\w+)$/; 596 $upage = uc($1); 597 print FILE ".\\\" Generated by gendoc.pl 598.Dd $TODAY 599.Dt $upage 3 600.Os 601.Sh NAME 602"; 603 my @funcs = sort(keys(%FUNCTIONS)); 604 while ($func = shift(@funcs)) { 605 print FILE ".Nm $FUNCTIONS{$func}->{Nm}"; 606 print FILE " ," 607 if (@funcs); 608 print FILE "\n"; 609 } 610 print FILE ".Nd Pluggable Authentication Modules Library 611.Sh LIBRARY 612.Lb libpam 613.Sh SYNOPSIS\n"; 614 if ($page eq 'pam') { 615 print FILE ".In security/pam_appl.h\n"; 616 } else { 617 print FILE ".In security/openpam.h\n"; 618 } 619 foreach $func (sort(keys(%FUNCTIONS))) { 620 print FILE ".Ft $FUNCTIONS{$func}->{Ft}\n"; 621 print FILE ".Fn $FUNCTIONS{$func}->{Fn}\n"; 622 } 623 while (<STDIN>) { 624 if (m/^\.Xr (\S+)\s*(\d)\s*$/) { 625 ++$xref{int($2)}->{$1}; 626 } 627 print FILE $_; 628 } 629 630 if ($page eq 'pam') { 631 print FILE ".Sh RETURN VALUES 632The following return codes are defined by 633.In security/pam_constants.h : 634.Bl -tag -width 18n 635"; 636 foreach (sort(keys(%PAMERR))) { 637 print FILE ".It Bq Er $_\n$PAMERR{$_}.\n"; 638 } 639 print FILE ".El\n"; 640 } 641 print FILE ".Sh SEE ALSO 642"; 643 if ($page eq 'pam') { 644 ++$xref{3}->{openpam}; 645 } 646 foreach $func (keys(%FUNCTIONS)) { 647 ++$xref{3}->{$func}; 648 } 649 print FILE genxref(\%xref); 650 print FILE ".Sh STANDARDS 651.Rs 652.%T \"X/Open Single Sign-On Service (XSSO) - Pluggable Authentication Modules\" 653.%D \"June 1997\" 654.Re 655"; 656 print FILE ".Sh AUTHORS 657The OpenPAM library and this manual page were $AUTHORS{THINKSEC} 658"; 659 close(FILE); 660} 661 662sub usage() { 663 664 print(STDERR "usage: gendoc [-op] source [...]\n"); 665 exit(1); 666} 667 668MAIN:{ 669 my %opts; 670 671 usage() 672 unless (@ARGV && getopts("op", \%opts)); 673 $TODAY = strftime("%B %e, %Y", localtime(time())); 674 $TODAY =~ s,\s+, ,g; 675 if ($opts{o} || $opts{p}) { 676 foreach my $fn (@ARGV) { 677 readproto($fn); 678 } 679 gensummary('openpam') 680 if ($opts{o}); 681 gensummary('pam') 682 if ($opts{p}); 683 } else { 684 foreach my $fn (@ARGV) { 685 my $func = parse_source($fn); 686 gendoc($func) 687 if (defined($func)); 688 } 689 } 690 exit(0); 691} 692