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