1#!/usr/bin/env perl 2#*************************************************************************** 3# _ _ ____ _ 4# Project ___| | | | _ \| | 5# / __| | | | |_) | | 6# | (__| |_| | _ <| |___ 7# \___|\___/|_| \_\_____| 8# 9# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 10# 11# This software is licensed as described in the file COPYING, which 12# you should have received as part of this distribution. The terms 13# are also available at https://curl.se/docs/copyright.html. 14# 15# You may opt to use, copy, modify, merge, publish, distribute and/or sell 16# copies of the Software, and permit persons to whom the Software is 17# furnished to do so, under the terms of the COPYING file. 18# 19# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 20# KIND, either express or implied. 21# 22# SPDX-License-Identifier: curl 23# 24########################################################################### 25 26=begin comment 27 28This script generates the manpage. 29 30Example: managen <command> [files] > curl.1 31 32Dev notes: 33 34We open *input* files in :crlf translation (a no-op on many platforms) in 35case we have CRLF line endings in Windows but a perl that defaults to LF. 36Unfortunately it seems some perls like msysgit cannot handle a global input-only 37:crlf so it has to be specified on each file open for text input. 38 39=end comment 40=cut 41 42my %optshort; 43my %optlong; 44my %helplong; 45my %arglong; 46my %redirlong; 47my %protolong; 48my %catlong; 49 50use POSIX qw(strftime); 51my @ts; 52if (defined($ENV{SOURCE_DATE_EPOCH})) { 53 @ts = gmtime($ENV{SOURCE_DATE_EPOCH}); 54} else { 55 @ts = localtime; 56} 57my $date = strftime "%Y-%m-%d", @ts; 58my $year = strftime "%Y", @ts; 59my $version = "unknown"; 60my $globals; 61 62# get the long name version, return the man page string 63sub manpageify { 64 my ($k)=@_; 65 my $l; 66 my $trail; 67 # the matching pattern might include a trailing dot that cannot be part of 68 # the option name 69 if($k =~ s/\.$//) { 70 # cut off trailing dot 71 $trail = "."; 72 } 73 my $klong = $k; 74 # quote "bare" minuses in the long name 75 $klong =~ s/-/\\-/g; 76 if($optlong{$k}) { 77 # both short + long 78 $l = "\\fI-".$optlong{$k}.", \\-\\-$klong\\fP$trail"; 79 } 80 else { 81 # only long 82 $l = "\\fI\\-\\-$klong\\fP$trail"; 83 } 84 return $l; 85} 86 87 88my $colwidth=78; # max number of columns 89 90sub justline { 91 my ($lvl, @line) = @_; 92 my $w = -1; 93 my $spaces = -1; 94 my $width = $colwidth - ($lvl * 4); 95 for(@line) { 96 $w += length($_); 97 $w++; 98 $spaces++; 99 } 100 my $inject = $width - $w; 101 my $ratio = 0; # stay at zero if no spaces at all 102 if($spaces) { 103 $ratio = $inject / $spaces; 104 } 105 my $spare = 0; 106 print ' ' x ($lvl * 4); 107 my $prev; 108 for(@line) { 109 while($spare >= 0.90) { 110 print " "; 111 $spare--; 112 } 113 printf "%s%s", $prev?" ":"", $_; 114 $prev = 1; 115 $spare += $ratio; 116 } 117 print "\n"; 118} 119 120sub lastline { 121 my ($lvl, @line) = @_; 122 print ' ' x ($lvl * 4); 123 my $prev = 0; 124 for(@line) { 125 printf "%s%s", $prev?" ":"", $_; 126 $prev = 1; 127 } 128 print "\n"; 129} 130 131sub outputpara { 132 my ($lvl, $f) = @_; 133 $f =~ s/\n/ /g; 134 135 my $w = 0; 136 my @words = split(/ */, $f); 137 my $width = $colwidth - ($lvl * 4); 138 139 my @line; 140 for my $e (@words) { 141 my $l = length($e); 142 my $spaces = scalar(@line); 143 if(($w + $l + $spaces) >= $width) { 144 justline($lvl, @line); 145 undef @line; 146 $w = 0; 147 } 148 149 push @line, $e; 150 $w += $l; # new width 151 } 152 if($w) { 153 lastline($lvl, @line); 154 print "\n"; 155 } 156} 157 158sub printdesc { 159 my ($manpage, $baselvl, @desc) = @_; 160 161 if($manpage) { 162 for my $d (@desc) { 163 print $d; 164 } 165 } 166 else { 167 my $p = -1; 168 my $para; 169 for my $l (@desc) { 170 my $lvl; 171 if($l !~ /^[\n\r]+/) { 172 # get the indent level off the string 173 $l =~ s/^\[([0-9q]*)\]//; 174 $lvl = $1; 175 } 176 if(($p =~ /q/) && ($lvl !~ /q/)) { 177 # the previous was quoted, this is not 178 print "\n"; 179 } 180 if($lvl != $p) { 181 outputpara($baselvl + $p, $para); 182 $para = ""; 183 } 184 if($lvl =~ /q/) { 185 # quoted, do not right-justify 186 chomp $l; 187 lastline($baselvl + $lvl + 1, $l); 188 } 189 else { 190 $para .= $l; 191 } 192 193 $p = $lvl; 194 } 195 outputpara($baselvl + $p, $para); 196 } 197} 198 199sub seealso { 200 my($standalone, $data)=@_; 201 if($standalone) { 202 return sprintf 203 ".SH \"SEE ALSO\"\n$data\n"; 204 } 205 else { 206 return "See also $data. "; 207 } 208} 209 210sub overrides { 211 my ($standalone, $data)=@_; 212 if($standalone) { 213 return ".SH \"OVERRIDES\"\n$data\n"; 214 } 215 else { 216 return $data; 217 } 218} 219 220sub protocols { 221 my ($manpage, $standalone, $data)=@_; 222 if($standalone) { 223 return ".SH \"PROTOCOLS\"\n$data\n"; 224 } 225 else { 226 return "($data) " if($manpage); 227 return "[1]($data) " if(!$manpage); 228 } 229} 230 231sub too_old { 232 my ($version)=@_; 233 my $a = 999999; 234 if($version =~ /^(\d+)\.(\d+)\.(\d+)/) { 235 $a = $1 * 1000 + $2 * 10 + $3; 236 } 237 elsif($version =~ /^(\d+)\.(\d+)/) { 238 $a = $1 * 1000 + $2 * 10; 239 } 240 if($a < 7500) { 241 # we consider everything before 7.50.0 to be too old to mention 242 # specific changes for 243 return 1; 244 } 245 return 0; 246} 247 248sub added { 249 my ($standalone, $data)=@_; 250 if(too_old($data)) { 251 # do not mention ancient additions 252 return ""; 253 } 254 if($standalone) { 255 return ".SH \"ADDED\"\nAdded in curl version $data\n"; 256 } 257 else { 258 return "Added in $data. "; 259 } 260} 261 262sub render { 263 my ($manpage, $fh, $f, $line) = @_; 264 my @desc; 265 my $tablemode = 0; 266 my $header = 0; 267 # if $top is TRUE, it means a top-level page and not a command line option 268 my $top = ($line == 1); 269 my $quote; 270 my $level; 271 $start = 0; 272 273 while(<$fh>) { 274 my $d = $_; 275 $line++; 276 if($d =~ /^\.(SH|BR|IP|B)/) { 277 print STDERR "$f:$line:1:ERROR: nroff instruction in input: \".$1\"\n"; 278 return 4; 279 } 280 if(/^ *<!--/) { 281 # skip comments 282 next; 283 } 284 if((!$start) && ($_ =~ /^[\r\n]*\z/)) { 285 # skip leading blank lines 286 next; 287 } 288 $start = 1; 289 if(/^# (.*)/) { 290 $header = 1; 291 if($top != 1) { 292 # ignored for command line options 293 $blankline++; 294 next; 295 } 296 push @desc, ".SH $1\n" if($manpage); 297 push @desc, "[0]$1\n" if(!$manpage); 298 next; 299 } 300 elsif(/^###/) { 301 print STDERR "$f:$line:1:ERROR: ### header is not supported\n"; 302 exit 3; 303 } 304 elsif(/^## (.*)/) { 305 my $word = $1; 306 # if there are enclosing quotes, remove them first 307 $word =~ s/[\"\'](.*)[\"\']\z/$1/; 308 309 # remove backticks from headers 310 $words =~ s/\`//g; 311 312 # if there is a space, it needs quotes for man page 313 if(($word =~ / /) && $manpage) { 314 $word = "\"$word\""; 315 } 316 $level = 1; 317 if($top == 1) { 318 push @desc, ".IP $word\n" if($manpage); 319 push @desc, "\n" if(!$manpage); 320 push @desc, "[1]$word\n" if(!$manpage); 321 } 322 else { 323 if(!$tablemode) { 324 push @desc, ".RS\n" if($manpage); 325 $tablemode = 1; 326 } 327 push @desc, ".IP $word\n" if($manpage); 328 push @desc, "\n" if(!$manpage); 329 push @desc, "[1]$word\n" if(!$manpage); 330 } 331 $header = 1; 332 next; 333 } 334 elsif(/^##/) { 335 if($top == 1) { 336 print STDERR "$f:$line:1:ERROR: ## empty header top-level mode\n"; 337 exit 3; 338 } 339 if($tablemode) { 340 # end of table 341 push @desc, ".RE\n.IP\n"; 342 $tablemode = 0; 343 } 344 $header = 1; 345 next; 346 } 347 elsif(/^\.(IP|RS|RE)/) { 348 my ($cmd) = ($1); 349 print STDERR "$f:$line:1:ERROR: $cmd detected, use ##-style\n"; 350 return 3; 351 } 352 elsif(/^[ \t]*\n/) { 353 # count and ignore blank lines 354 $blankline++; 355 next; 356 } 357 elsif($d =~ /^ (.*)/) { 358 my $word = $1; 359 if(!$quote && $manpage) { 360 push @desc, ".nf\n"; 361 } 362 $quote = 1; 363 $d = "$word\n"; 364 } 365 elsif($quote && ($d !~ /^ (.*)/)) { 366 # end of quote 367 push @desc, ".fi\n" if($manpage); 368 $quote = 0; 369 } 370 371 $d =~ s/`%DATE`/$date/g; 372 $d =~ s/`%VERSION`/$version/g; 373 $d =~ s/`%GLOBALS`/$globals/g; 374 375 # convert backticks to double quotes 376 $d =~ s/\`/\"/g; 377 378 if($d =~ /\(Added in ([0-9.]+)\)/i) { 379 my $ver = $1; 380 if(too_old($ver)) { 381 $d =~ s/ *\(Added in $ver\)//gi; 382 } 383 } 384 385 if(!$quote) { 386 if($d =~ /^(.*) /) { 387 printf STDERR "$f:$line:%d:ERROR: 2 spaces detected\n", 388 length($1); 389 return 3; 390 } 391 elsif($d =~ /[^\\][\<\>]/) { 392 print STDERR "$f:$line:1:WARN: un-escaped < or > used\n"; 393 return 3; 394 } 395 } 396 # convert backslash-'<' or '> to just the second character 397 $d =~ s/\\([><])/$1/g; 398 # convert single backslash to double-backslash 399 $d =~ s/\\/\\\\/g if($manpage); 400 401 402 if($manpage) { 403 if(!$quote && $d =~ /--/) { 404 $d =~ s/--([a-z0-9.-]+)/manpageify($1)/ge; 405 } 406 407 # quote minuses in the output 408 $d =~ s/([^\\])-/$1\\-/g; 409 # replace single quotes 410 $d =~ s/\'/\\(aq/g; 411 # handle double quotes or periods first on the line 412 $d =~ s/^([\.\"])/\\&$1/; 413 # **bold** 414 $d =~ s/\*\*(\S.*?)\*\*/\\fB$1\\fP/g; 415 # *italics* 416 $d =~ s/\*(\S.*?)\*/\\fI$1\\fP/g; 417 } 418 else { 419 # **bold** 420 $d =~ s/\*\*(\S.*?)\*\*/$1/g; 421 # *italics* 422 $d =~ s/\*(\S.*?)\*/$1/g; 423 } 424 # trim trailing spaces 425 $d =~ s/[ \t]+\z//; 426 push @desc, "\n" if($blankline && !$header); 427 $blankline = 0; 428 push @desc, $d if($manpage); 429 my $qstr = $quote ? "q": ""; 430 push @desc, "[".(1 + $level)."$qstr]$d" if(!$manpage); 431 $header = 0; 432 433 } 434 if($tablemode) { 435 # end of table 436 push @desc, ".RE\n.IP\n" if($manpage); 437 } 438 return @desc; 439} 440 441sub single { 442 my ($dir, $manpage, $f, $standalone)=@_; 443 my $fh; 444 open($fh, "<:crlf", "$dir/$f") || 445 die "could not find $dir/$f"; 446 my $short; 447 my $long; 448 my $tags; 449 my $added; 450 my $protocols; 451 my $arg; 452 my $mutexed; 453 my $requires; 454 my $category; 455 my @seealso; 456 my $copyright; 457 my $spdx; 458 my @examples; # there can be more than one 459 my $magic; # cmdline special option 460 my $line; 461 my $dline; 462 my $multi; 463 my $scope; 464 my $experimental; 465 my $start; 466 my $list; # identifies the list, 1 example, 2 see-also 467 while(<$fh>) { 468 $line++; 469 if(/^ *<!--/) { 470 next; 471 } 472 if(!$start) { 473 if(/^---/) { 474 $start = 1; 475 } 476 next; 477 } 478 if(/^Short: *(.)/i) { 479 $short=$1; 480 } 481 elsif(/^Long: *(.*)/i) { 482 $long=$1; 483 } 484 elsif(/^Added: *(.*)/i) { 485 $added=$1; 486 } 487 elsif(/^Tags: *(.*)/i) { 488 $tags=$1; 489 } 490 elsif(/^Arg: *(.*)/i) { 491 $arg=$1; 492 } 493 elsif(/^Magic: *(.*)/i) { 494 $magic=$1; 495 } 496 elsif(/^Mutexed: *(.*)/i) { 497 $mutexed=$1; 498 } 499 elsif(/^Protocols: *(.*)/i) { 500 $protocols=$1; 501 } 502 elsif(/^See-also: +(.+)/i) { 503 if($seealso) { 504 print STDERR "ERROR: duplicated See-also in $f\n"; 505 return 1; 506 } 507 push @seealso, $1; 508 } 509 elsif(/^See-also:/i) { 510 $list=2; 511 } 512 elsif(/^ *- (.*)/i && ($list == 2)) { 513 push @seealso, $1; 514 } 515 elsif(/^Requires: *(.*)/i) { 516 $requires=$1; 517 } 518 elsif(/^Category: *(.*)/i) { 519 $category=$1; 520 } 521 elsif(/^Example: +(.+)/i) { 522 push @examples, $1; 523 } 524 elsif(/^Example:/i) { 525 # '1' is the example list 526 $list = 1; 527 } 528 elsif(/^ *- (.*)/i && ($list == 1)) { 529 push @examples, $1; 530 } 531 elsif(/^Multi: *(.*)/i) { 532 $multi=$1; 533 } 534 elsif(/^Scope: *(.*)/i) { 535 $scope=$1; 536 } 537 elsif(/^Experimental: yes/i) { 538 $experimental=1; 539 } 540 elsif(/^C: (.*)/i) { 541 $copyright=$1; 542 } 543 elsif(/^SPDX-License-Identifier: (.*)/i) { 544 $spdx=$1; 545 } 546 elsif(/^Help: *(.*)/i) { 547 ; 548 } 549 elsif(/^---/) { 550 $start++; 551 if(!$long) { 552 print STDERR "ERROR: no 'Long:' in $f\n"; 553 return 1; 554 } 555 if(!$category) { 556 print STDERR "ERROR: no 'Category:' in $f\n"; 557 return 2; 558 } 559 if(!$examples[0]) { 560 print STDERR "$f:$line:1:ERROR: no 'Example:' present\n"; 561 return 2; 562 } 563 if(!$added) { 564 print STDERR "$f:$line:1:ERROR: no 'Added:' version present\n"; 565 return 2; 566 } 567 if(!$seealso[0]) { 568 print STDERR "$f:$line:1:ERROR: no 'See-also:' field present\n"; 569 return 2; 570 } 571 if(!$copyright) { 572 print STDERR "$f:$line:1:ERROR: no 'C:' field present\n"; 573 return 2; 574 } 575 if(!$spdx) { 576 print STDERR "$f:$line:1:ERROR: no 'SPDX-License-Identifier:' field present\n"; 577 return 2; 578 } 579 last; 580 } 581 else { 582 chomp; 583 print STDERR "$f:$line:1:WARN: unrecognized line in $f, ignoring:\n:'$_';" 584 } 585 } 586 587 if($start < 2) { 588 print STDERR "$f:1:1:ERROR: no proper meta-data header\n"; 589 return 2; 590 } 591 592 my @desc = render($manpage, $fh, $f, $line); 593 close($fh); 594 if($tablemode) { 595 # end of table 596 push @desc, ".RE\n.IP\n"; 597 } 598 my $opt; 599 600 if(defined($short) && $long) { 601 $opt = "-$short, --$long"; 602 } 603 elsif($short && !$long) { 604 $opt = "-$short"; 605 } 606 elsif($long && !$short) { 607 $opt = "--$long"; 608 } 609 610 if($arg) { 611 $opt .= " $arg"; 612 } 613 614 # quote "bare" minuses in opt 615 $opt =~ s/-/\\-/g if($manpage); 616 if($standalone) { 617 print ".TH curl 1 \"30 Nov 2016\" \"curl 7.52.0\" \"curl manual\"\n"; 618 print ".SH OPTION\n"; 619 print "curl $opt\n"; 620 } 621 elsif($manpage) { 622 print ".IP \"$opt\"\n"; 623 } 624 else { 625 lastline(1, $opt); 626 } 627 my @leading; 628 if($protocols) { 629 push @leading, protocols($manpage, $standalone, $protocols); 630 } 631 632 if($standalone) { 633 print ".SH DESCRIPTION\n"; 634 } 635 636 if($experimental) { 637 push @leading, "**WARNING**: this option is experimental. Do not use in production.\n\n"; 638 } 639 640 my $pre = $manpage ? "\n": "[1]"; 641 642 if($scope) { 643 if($scope eq "global") { 644 push @desc, "\n" if(!$manpage); 645 push @desc, "${pre}This option is global and does not need to be specified for each use of --next.\n"; 646 } 647 else { 648 print STDERR "$f:$line:1:ERROR: unrecognized scope: '$scope'\n"; 649 return 2; 650 } 651 } 652 653 printdesc($manpage, 2, (@leading, @desc)); 654 undef @desc; 655 656 my @extra; 657 if($multi eq "single") { 658 push @extra, "${pre}If --$long is provided several times, the last set ". 659 "value is used.\n"; 660 } 661 elsif($multi eq "append") { 662 push @extra, "${pre}--$long can be used several times in a command line\n"; 663 } 664 elsif($multi eq "boolean") { 665 my $rev = "no-$long"; 666 # for options that start with "no-" the reverse is then without 667 # the no- prefix 668 if($long =~ /^no-/) { 669 $rev = $long; 670 $rev =~ s/^no-//; 671 } 672 my $dashes = $manpage ? "\\-\\-" : "--"; 673 push @extra, 674 "${pre}Providing --$long multiple times has no extra effect.\n". 675 "Disable it again with $dashes$rev.\n"; 676 } 677 elsif($multi eq "mutex") { 678 push @extra, 679 "${pre}Providing --$long multiple times has no extra effect.\n"; 680 } 681 elsif($multi eq "custom") { 682 ; # left for the text to describe 683 } 684 else { 685 print STDERR "$f:$line:1:ERROR: unrecognized Multi: '$multi'\n"; 686 return 2; 687 } 688 689 printdesc($manpage, 2, @extra); 690 691 my @foot; 692 693 my $mstr; 694 my $and = 0; 695 my $num = scalar(@seealso); 696 if($num > 2) { 697 # use commas up to this point 698 $and = $num - 1; 699 } 700 my $i = 0; 701 for my $k (@seealso) { 702 if(!$helplong{$k}) { 703 print STDERR "$f:$line:1:WARN: see-also a non-existing option: $k\n"; 704 } 705 my $l = $manpage ? manpageify($k) : "--$k"; 706 my $sep = " and"; 707 if($and && ($i < $and)) { 708 $sep = ","; 709 } 710 $mstr .= sprintf "%s$l", $mstr?"$sep ":""; 711 $i++; 712 } 713 push @foot, seealso($standalone, $mstr); 714 715 if($requires) { 716 my $l = $manpage ? manpageify($long) : "--$long"; 717 push @foot, "$l requires that the underlying libcurl". 718 " was built to support $requires. "; 719 } 720 if($mutexed) { 721 my @m=split(/ /, $mutexed); 722 my $mstr; 723 for my $k (@m) { 724 if(!$helplong{$k}) { 725 print STDERR "WARN: $f mutexes a non-existing option: $k\n"; 726 } 727 my $l = $manpage ? manpageify($k) : "--$k"; 728 $mstr .= sprintf "%s$l", $mstr?" and ":""; 729 } 730 push @foot, overrides($standalone, 731 "This option is mutually exclusive to $mstr. "); 732 } 733 if($examples[0]) { 734 my $s =""; 735 $s="s" if($examples[1]); 736 if($manpage) { 737 print "\nExample$s:\n"; 738 print ".nf\n"; 739 foreach my $e (@examples) { 740 $e =~ s!\$URL!https://example.com!g; 741 # convert single backslahes to doubles 742 $e =~ s/\\/\\\\/g; 743 print " curl $e\n"; 744 } 745 print ".fi\n"; 746 } 747 else { 748 my @ex; 749 push @ex, "[0q]Example$s:\n"; 750 foreach my $e (@examples) { 751 $e =~ s!\$URL!https://example.com!g; 752 push @ex, "[0q] curl $e\n"; 753 } 754 printdesc($manpage, 2, @ex); 755 } 756 } 757 if($added) { 758 push @foot, added($standalone, $added); 759 } 760 if($foot[0]) { 761 print "\n"; 762 my $f = join("", @foot); 763 if($manpage) { 764 $f =~ s/ +\z//; # remove trailing space 765 print "$f\n"; 766 } 767 else { 768 printdesc($manpage, 2, "[1]$f"); 769 } 770 } 771 return 0; 772} 773 774sub getshortlong { 775 my ($dir, $f)=@_; 776 $f =~ s/^.*\///; 777 open(F, "<:crlf", "$dir/$f") || 778 die "could not find $dir/$f"; 779 my $short; 780 my $long; 781 my $help; 782 my $arg; 783 my $protocols; 784 my $category; 785 my $start = 0; 786 while(<F>) { 787 if(!$start) { 788 if(/^---/) { 789 $start = 1; 790 } 791 next; 792 } 793 if(/^Short: (.)/i) { 794 $short=$1; 795 } 796 elsif(/^Long: (.*)/i) { 797 $long=$1; 798 } 799 elsif(/^Help: (.*)/i) { 800 $help=$1; 801 } 802 elsif(/^Arg: (.*)/i) { 803 $arg=$1; 804 } 805 elsif(/^Protocols: (.*)/i) { 806 $protocols=$1; 807 } 808 elsif(/^Category: (.*)/i) { 809 $category=$1; 810 } 811 elsif(/^---/) { 812 last; 813 } 814 } 815 close(F); 816 if($short) { 817 $optshort{$short}=$long; 818 } 819 if($long) { 820 $optlong{$long}=$short; 821 $helplong{$long}=$help; 822 $arglong{$long}=$arg; 823 $protolong{$long}=$protocols; 824 $catlong{$long}=$category; 825 } 826} 827 828sub indexoptions { 829 my ($dir, @files) = @_; 830 foreach my $f (@files) { 831 getshortlong($dir, $f); 832 } 833} 834 835sub header { 836 my ($dir, $manpage, $f)=@_; 837 my $fh; 838 open($fh, "<:crlf", "$dir/$f") || 839 die "could not find $dir/$f"; 840 my @d = render($manpage, $fh, $f, 1); 841 close($fh); 842 printdesc($manpage, 0, @d); 843} 844 845sub listhelp { 846 print <<HEAD 847/*************************************************************************** 848 * _ _ ____ _ 849 * Project ___| | | | _ \\| | 850 * / __| | | | |_) | | 851 * | (__| |_| | _ <| |___ 852 * \\___|\\___/|_| \\_\\_____| 853 * 854 * Copyright (C) Daniel Stenberg, <daniel\@haxx.se>, et al. 855 * 856 * This software is licensed as described in the file COPYING, which 857 * you should have received as part of this distribution. The terms 858 * are also available at https://curl.se/docs/copyright.html. 859 * 860 * You may opt to use, copy, modify, merge, publish, distribute and/or sell 861 * copies of the Software, and permit persons to whom the Software is 862 * furnished to do so, under the terms of the COPYING file. 863 * 864 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 865 * KIND, either express or implied. 866 * 867 * SPDX-License-Identifier: curl 868 * 869 ***************************************************************************/ 870#include "tool_setup.h" 871#include "tool_help.h" 872 873/* 874 * DO NOT edit tool_listhelp.c manually. 875 * This source file is generated with the following command in an autotools 876 * build: 877 * 878 * "make listhelp" 879 */ 880 881const struct helptxt helptext[] = { 882HEAD 883 ; 884 foreach my $f (sort keys %helplong) { 885 my $long = $f; 886 my $short = $optlong{$long}; 887 my @categories = split ' ', $catlong{$long}; 888 my $bitmask = ' '; 889 my $opt; 890 891 if(defined($short) && $long) { 892 $opt = "-$short, --$long"; 893 } 894 elsif($long && !$short) { 895 $opt = " --$long"; 896 } 897 for my $i (0 .. $#categories) { 898 $bitmask .= 'CURLHELP_' . uc $categories[$i]; 899 # If not last element, append | 900 if($i < $#categories) { 901 $bitmask .= ' | '; 902 } 903 } 904 $bitmask =~ s/(?=.{76}).{1,76}\|/$&\n /g; 905 my $arg = $arglong{$long}; 906 if($arg) { 907 $opt .= " $arg"; 908 } 909 my $desc = $helplong{$f}; 910 $desc =~ s/\"/\\\"/g; # escape double quotes 911 912 my $line = sprintf " {\"%s\",\n \"%s\",\n %s},\n", $opt, $desc, $bitmask; 913 914 if(length($opt) > 78) { 915 print STDERR "WARN: the --$long name is too long\n"; 916 } 917 elsif(length($desc) > 78) { 918 print STDERR "WARN: the --$long description is too long\n"; 919 } 920 print $line; 921 } 922 print <<FOOT 923 { NULL, NULL, CURLHELP_HIDDEN } 924}; 925FOOT 926 ; 927} 928 929sub listcats { 930 my %allcats; 931 foreach my $f (sort keys %helplong) { 932 my @categories = split ' ', $catlong{$f}; 933 foreach (@categories) { 934 $allcats{$_} = undef; 935 } 936 } 937 my @categories; 938 foreach my $key (keys %allcats) { 939 push @categories, $key; 940 } 941 @categories = sort @categories; 942 unshift @categories, 'hidden'; 943 for my $i (0..$#categories) { 944 print '#define ' . 'CURLHELP_' . uc($categories[$i]) . ' ' . "1u << " . $i . "u\n"; 945 } 946} 947 948sub listglobals { 949 my ($dir, @files) = @_; 950 my @globalopts; 951 952 # Find all global options and output them 953 foreach my $f (sort @files) { 954 open(F, "<:crlf", "$dir/$f") || 955 die "could not read $dir/$f"; 956 my $long; 957 my $start = 0; 958 while(<F>) { 959 if(/^---/) { 960 if(!$start) { 961 $start = 1; 962 next; 963 } 964 else { 965 last; 966 } 967 } 968 if(/^Long: *(.*)/i) { 969 $long=$1; 970 } 971 elsif(/^Scope: global/i) { 972 push @globalopts, $long; 973 last; 974 } 975 } 976 close(F); 977 } 978 return $ret if($ret); 979 for my $e (0 .. $#globalopts) { 980 $globals .= sprintf "%s--%s", $e?($globalopts[$e+1] ? ", " : " and "):"", 981 $globalopts[$e],; 982 } 983} 984 985sub noext { 986 my $in = $_[0]; 987 $in =~ s/\.d//; 988 return $in; 989} 990 991sub sortnames { 992 return noext($a) cmp noext($b); 993} 994 995sub mainpage { 996 my ($dir, $manpage, @files) = @_; 997 # $manpage is 1 for nroff, 0 for ASCII 998 my $ret; 999 my $fh; 1000 open($fh, "<:crlf", "$dir/mainpage.idx") || 1001 die "no $dir/mainpage.idx file"; 1002 1003 print <<HEADER 1004.\\" ************************************************************************** 1005.\\" * _ _ ____ _ 1006.\\" * Project ___| | | | _ \\| | 1007.\\" * / __| | | | |_) | | 1008.\\" * | (__| |_| | _ <| |___ 1009.\\" * \\___|\\___/|_| \\_\\_____| 1010.\\" * 1011.\\" * Copyright (C) Daniel Stenberg, <daniel\@haxx.se>, et al. 1012.\\" * 1013.\\" * This software is licensed as described in the file COPYING, which 1014.\\" * you should have received as part of this distribution. The terms 1015.\\" * are also available at https://curl.se/docs/copyright.html. 1016.\\" * 1017.\\" * You may opt to use, copy, modify, merge, publish, distribute and/or sell 1018.\\" * copies of the Software, and permit persons to whom the Software is 1019.\\" * furnished to do so, under the terms of the COPYING file. 1020.\\" * 1021.\\" * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 1022.\\" * KIND, either express or implied. 1023.\\" * 1024.\\" * SPDX-License-Identifier: curl 1025.\\" * 1026.\\" ************************************************************************** 1027.\\" 1028.\\" DO NOT EDIT. Generated by the curl project managen man page generator. 1029.\\" 1030.TH curl 1 "$date" "curl $version" "curl Manual" 1031HEADER 1032 if ($manpage); 1033 1034 while(<$fh>) { 1035 my $f = $_; 1036 chomp $f; 1037 if($f =~ /^#/) { 1038 # standard comment 1039 next; 1040 } 1041 if(/^%options/) { 1042 # output docs for all options 1043 foreach my $f (sort sortnames @files) { 1044 $ret += single($dir, $manpage, $f, 0); 1045 } 1046 } 1047 else { 1048 # render the file 1049 header($dir, $manpage, $f); 1050 } 1051 } 1052 close($fh); 1053 exit $ret if($ret); 1054} 1055 1056sub showonly { 1057 my ($f) = @_; 1058 if(single($f, 1)) { 1059 print STDERR "$f: failed\n"; 1060 } 1061} 1062 1063sub showprotocols { 1064 my %prots; 1065 foreach my $f (keys %optlong) { 1066 my @p = split(/ /, $protolong{$f}); 1067 for my $p (@p) { 1068 $prots{$p}++; 1069 } 1070 } 1071 for(sort keys %prots) { 1072 printf "$_ (%d options)\n", $prots{$_}; 1073 } 1074} 1075 1076sub getargs { 1077 my ($dir, $f, @s) = @_; 1078 if($f eq "mainpage") { 1079 listglobals($dir, @s); 1080 mainpage($dir, 1, @s); 1081 return; 1082 } 1083 elsif($f eq "ascii") { 1084 listglobals($dir, @s); 1085 mainpage($dir, 0, @s); 1086 return; 1087 } 1088 elsif($f eq "listhelp") { 1089 listhelp(); 1090 return; 1091 } 1092 elsif($f eq "single") { 1093 showonly($s[0]); 1094 return; 1095 } 1096 elsif($f eq "protos") { 1097 showprotocols(); 1098 return; 1099 } 1100 elsif($f eq "listcats") { 1101 listcats(); 1102 return; 1103 } 1104 1105 print "Usage: managen ". 1106 "[-d dir] <mainpage/ascii/listhelp/single FILE/protos/listcats> [files]\n"; 1107} 1108 1109#------------------------------------------------------------------------ 1110 1111my $dir = "."; 1112my $include = "../../include"; 1113my $cmd = shift @ARGV; 1114 1115 check: 1116if($cmd eq "-d") { 1117 # specifies source directory 1118 $dir = shift @ARGV; 1119 $cmd = shift @ARGV; 1120 goto check; 1121} 1122elsif($cmd eq "-I") { 1123 # include path root 1124 $include = shift @ARGV; 1125 $cmd = shift @ARGV; 1126 goto check; 1127} 1128 1129my @files = @ARGV; # the rest are the files 1130 1131open(INC, "<$include/curl/curlver.h"); 1132while(<INC>) { 1133 if($_ =~ /^#define LIBCURL_VERSION \"([0-9.]*)/) { 1134 $version = $1; 1135 last; 1136 } 1137} 1138close(INC); 1139 1140# learn all existing options 1141indexoptions($dir, @files); 1142 1143getargs($dir, $cmd, @files); 1144