| #!/usr/bin/env perl |
| #*************************************************************************** |
| # _ _ ____ _ |
| # Project ___| | | | _ \| | |
| # / __| | | | |_) | | |
| # | (__| |_| | _ <| |___ |
| # \___|\___/|_| \_\_____| |
| # |
| # Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. |
| # |
| # This software is licensed as described in the file COPYING, which |
| # you should have received as part of this distribution. The terms |
| # are also available at https://curl.se/docs/copyright.html. |
| # |
| # You may opt to use, copy, modify, merge, publish, distribute and/or sell |
| # copies of the Software, and permit persons to whom the Software is |
| # furnished to do so, under the terms of the COPYING file. |
| # |
| # This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
| # KIND, either express or implied. |
| # |
| # SPDX-License-Identifier: curl |
| # |
| ########################################################################### |
| |
| =begin comment |
| |
| This script generates the manpage. |
| |
| Example: managen <command> [files] > curl.1 |
| |
| Dev notes: |
| |
| We open *input* files in :crlf translation (a no-op on many platforms) in |
| case we have CRLF line endings in Windows but a perl that defaults to LF. |
| Unfortunately it seems some perls like msysgit cannot handle a global input-only |
| :crlf so it has to be specified on each file open for text input. |
| |
| =end comment |
| =cut |
| |
| my %optshort; |
| my %optlong; |
| my %helplong; |
| my %arglong; |
| my %redirlong; |
| my %protolong; |
| my %catlong; |
| |
| use POSIX qw(strftime); |
| my @ts; |
| if (defined($ENV{SOURCE_DATE_EPOCH})) { |
| @ts = gmtime($ENV{SOURCE_DATE_EPOCH}); |
| } else { |
| @ts = localtime; |
| } |
| my $date = strftime "%Y-%m-%d", @ts; |
| my $year = strftime "%Y", @ts; |
| my $version = "unknown"; |
| my $globals; |
| |
| # get the long name version, return the man page string |
| sub manpageify { |
| my ($k)=@_; |
| my $l; |
| my $trail; |
| # the matching pattern might include a trailing dot that cannot be part of |
| # the option name |
| if($k =~ s/\.$//) { |
| # cut off trailing dot |
| $trail = "."; |
| } |
| my $klong = $k; |
| # quote "bare" minuses in the long name |
| $klong =~ s/-/\\-/g; |
| if($optlong{$k}) { |
| # both short + long |
| $l = "\\fI-".$optlong{$k}.", \\-\\-$klong\\fP$trail"; |
| } |
| else { |
| # only long |
| $l = "\\fI\\-\\-$klong\\fP$trail"; |
| } |
| return $l; |
| } |
| |
| |
| my $colwidth=78; # max number of columns |
| |
| sub justline { |
| my ($lvl, @line) = @_; |
| my $w = -1; |
| my $spaces = -1; |
| my $width = $colwidth - ($lvl * 4); |
| for(@line) { |
| $w += length($_); |
| $w++; |
| $spaces++; |
| } |
| my $inject = $width - $w; |
| my $ratio = 0; # stay at zero if no spaces at all |
| if($spaces) { |
| $ratio = $inject / $spaces; |
| } |
| my $spare = 0; |
| print ' ' x ($lvl * 4); |
| my $prev; |
| for(@line) { |
| while($spare >= 0.90) { |
| print " "; |
| $spare--; |
| } |
| printf "%s%s", $prev?" ":"", $_; |
| $prev = 1; |
| $spare += $ratio; |
| } |
| print "\n"; |
| } |
| |
| sub lastline { |
| my ($lvl, @line) = @_; |
| print ' ' x ($lvl * 4); |
| my $prev = 0; |
| for(@line) { |
| printf "%s%s", $prev?" ":"", $_; |
| $prev = 1; |
| } |
| print "\n"; |
| } |
| |
| sub outputpara { |
| my ($lvl, $f) = @_; |
| $f =~ s/\n/ /g; |
| |
| my $w = 0; |
| my @words = split(/ */, $f); |
| my $width = $colwidth - ($lvl * 4); |
| |
| my @line; |
| for my $e (@words) { |
| my $l = length($e); |
| my $spaces = scalar(@line); |
| if(($w + $l + $spaces) >= $width) { |
| justline($lvl, @line); |
| undef @line; |
| $w = 0; |
| } |
| |
| push @line, $e; |
| $w += $l; # new width |
| } |
| if($w) { |
| lastline($lvl, @line); |
| print "\n"; |
| } |
| } |
| |
| sub printdesc { |
| my ($manpage, $baselvl, @desc) = @_; |
| |
| if($manpage) { |
| for my $d (@desc) { |
| print $d; |
| } |
| } |
| else { |
| my $p = -1; |
| my $para; |
| for my $l (@desc) { |
| my $lvl; |
| if($l !~ /^[\n\r]+/) { |
| # get the indent level off the string |
| $l =~ s/^\[([0-9q]*)\]//; |
| $lvl = $1; |
| } |
| if(($p =~ /q/) && ($lvl !~ /q/)) { |
| # the previous was quoted, this is not |
| print "\n"; |
| } |
| if($lvl != $p) { |
| outputpara($baselvl + $p, $para); |
| $para = ""; |
| } |
| if($lvl =~ /q/) { |
| # quoted, do not right-justify |
| chomp $l; |
| lastline($baselvl + $lvl + 1, $l); |
| } |
| else { |
| $para .= $l; |
| } |
| |
| $p = $lvl; |
| } |
| outputpara($baselvl + $p, $para); |
| } |
| } |
| |
| sub seealso { |
| my($standalone, $data)=@_; |
| if($standalone) { |
| return sprintf |
| ".SH \"SEE ALSO\"\n$data\n"; |
| } |
| else { |
| return "See also $data. "; |
| } |
| } |
| |
| sub overrides { |
| my ($standalone, $data)=@_; |
| if($standalone) { |
| return ".SH \"OVERRIDES\"\n$data\n"; |
| } |
| else { |
| return $data; |
| } |
| } |
| |
| sub protocols { |
| my ($manpage, $standalone, $data)=@_; |
| if($standalone) { |
| return ".SH \"PROTOCOLS\"\n$data\n"; |
| } |
| else { |
| return "($data) " if($manpage); |
| return "[1]($data) " if(!$manpage); |
| } |
| } |
| |
| sub too_old { |
| my ($version)=@_; |
| my $a = 999999; |
| if($version =~ /^(\d+)\.(\d+)\.(\d+)/) { |
| $a = $1 * 1000 + $2 * 10 + $3; |
| } |
| elsif($version =~ /^(\d+)\.(\d+)/) { |
| $a = $1 * 1000 + $2 * 10; |
| } |
| if($a < 7500) { |
| # we consider everything before 7.50.0 to be too old to mention |
| # specific changes for |
| return 1; |
| } |
| return 0; |
| } |
| |
| sub added { |
| my ($standalone, $data)=@_; |
| if(too_old($data)) { |
| # do not mention ancient additions |
| return ""; |
| } |
| if($standalone) { |
| return ".SH \"ADDED\"\nAdded in curl version $data\n"; |
| } |
| else { |
| return "Added in $data. "; |
| } |
| } |
| |
| sub render { |
| my ($manpage, $fh, $f, $line) = @_; |
| my @desc; |
| my $tablemode = 0; |
| my $header = 0; |
| # if $top is TRUE, it means a top-level page and not a command line option |
| my $top = ($line == 1); |
| my $quote; |
| my $level; |
| $start = 0; |
| |
| while(<$fh>) { |
| my $d = $_; |
| $line++; |
| if($d =~ /^\.(SH|BR|IP|B)/) { |
| print STDERR "$f:$line:1:ERROR: nroff instruction in input: \".$1\"\n"; |
| return 4; |
| } |
| if(/^ *<!--/) { |
| # skip comments |
| next; |
| } |
| if((!$start) && ($_ =~ /^[\r\n]*\z/)) { |
| # skip leading blank lines |
| next; |
| } |
| $start = 1; |
| if(/^# (.*)/) { |
| $header = 1; |
| if($top != 1) { |
| # ignored for command line options |
| $blankline++; |
| next; |
| } |
| push @desc, ".SH $1\n" if($manpage); |
| push @desc, "[0]$1\n" if(!$manpage); |
| next; |
| } |
| elsif(/^###/) { |
| print STDERR "$f:$line:1:ERROR: ### header is not supported\n"; |
| exit 3; |
| } |
| elsif(/^## (.*)/) { |
| my $word = $1; |
| # if there are enclosing quotes, remove them first |
| $word =~ s/[\"\'](.*)[\"\']\z/$1/; |
| |
| # remove backticks from headers |
| $words =~ s/\`//g; |
| |
| # if there is a space, it needs quotes for man page |
| if(($word =~ / /) && $manpage) { |
| $word = "\"$word\""; |
| } |
| $level = 1; |
| if($top == 1) { |
| push @desc, ".IP $word\n" if($manpage); |
| push @desc, "\n" if(!$manpage); |
| push @desc, "[1]$word\n" if(!$manpage); |
| } |
| else { |
| if(!$tablemode) { |
| push @desc, ".RS\n" if($manpage); |
| $tablemode = 1; |
| } |
| push @desc, ".IP $word\n" if($manpage); |
| push @desc, "\n" if(!$manpage); |
| push @desc, "[1]$word\n" if(!$manpage); |
| } |
| $header = 1; |
| next; |
| } |
| elsif(/^##/) { |
| if($top == 1) { |
| print STDERR "$f:$line:1:ERROR: ## empty header top-level mode\n"; |
| exit 3; |
| } |
| if($tablemode) { |
| # end of table |
| push @desc, ".RE\n.IP\n"; |
| $tablemode = 0; |
| } |
| $header = 1; |
| next; |
| } |
| elsif(/^\.(IP|RS|RE)/) { |
| my ($cmd) = ($1); |
| print STDERR "$f:$line:1:ERROR: $cmd detected, use ##-style\n"; |
| return 3; |
| } |
| elsif(/^[ \t]*\n/) { |
| # count and ignore blank lines |
| $blankline++; |
| next; |
| } |
| elsif($d =~ /^ (.*)/) { |
| my $word = $1; |
| if(!$quote && $manpage) { |
| push @desc, ".nf\n"; |
| } |
| $quote = 1; |
| $d = "$word\n"; |
| } |
| elsif($quote && ($d !~ /^ (.*)/)) { |
| # end of quote |
| push @desc, ".fi\n" if($manpage); |
| $quote = 0; |
| } |
| |
| $d =~ s/`%DATE`/$date/g; |
| $d =~ s/`%VERSION`/$version/g; |
| $d =~ s/`%GLOBALS`/$globals/g; |
| |
| # convert backticks to double quotes |
| $d =~ s/\`/\"/g; |
| |
| if($d =~ /\(Added in ([0-9.]+)\)/i) { |
| my $ver = $1; |
| if(too_old($ver)) { |
| $d =~ s/ *\(Added in $ver\)//gi; |
| } |
| } |
| |
| if(!$quote) { |
| if($d =~ /^(.*) /) { |
| printf STDERR "$f:$line:%d:ERROR: 2 spaces detected\n", |
| length($1); |
| return 3; |
| } |
| elsif($d =~ /[^\\][\<\>]/) { |
| print STDERR "$f:$line:1:WARN: un-escaped < or > used\n"; |
| return 3; |
| } |
| } |
| # convert backslash-'<' or '> to just the second character |
| $d =~ s/\\([><])/$1/g; |
| # convert single backslash to double-backslash |
| $d =~ s/\\/\\\\/g if($manpage); |
| |
| |
| if($manpage) { |
| if(!$quote && $d =~ /--/) { |
| $d =~ s/--([a-z0-9.-]+)/manpageify($1)/ge; |
| } |
| |
| # quote minuses in the output |
| $d =~ s/([^\\])-/$1\\-/g; |
| # replace single quotes |
| $d =~ s/\'/\\(aq/g; |
| # handle double quotes or periods first on the line |
| $d =~ s/^([\.\"])/\\&$1/; |
| # **bold** |
| $d =~ s/\*\*(\S.*?)\*\*/\\fB$1\\fP/g; |
| # *italics* |
| $d =~ s/\*(\S.*?)\*/\\fI$1\\fP/g; |
| } |
| else { |
| # **bold** |
| $d =~ s/\*\*(\S.*?)\*\*/$1/g; |
| # *italics* |
| $d =~ s/\*(\S.*?)\*/$1/g; |
| } |
| # trim trailing spaces |
| $d =~ s/[ \t]+\z//; |
| push @desc, "\n" if($blankline && !$header); |
| $blankline = 0; |
| push @desc, $d if($manpage); |
| my $qstr = $quote ? "q": ""; |
| push @desc, "[".(1 + $level)."$qstr]$d" if(!$manpage); |
| $header = 0; |
| |
| } |
| if($tablemode) { |
| # end of table |
| push @desc, ".RE\n.IP\n" if($manpage); |
| } |
| return @desc; |
| } |
| |
| sub single { |
| my ($dir, $manpage, $f, $standalone)=@_; |
| my $fh; |
| open($fh, "<:crlf", "$dir/$f") || |
| die "could not find $dir/$f"; |
| my $short; |
| my $long; |
| my $tags; |
| my $added; |
| my $protocols; |
| my $arg; |
| my $mutexed; |
| my $requires; |
| my $category; |
| my @seealso; |
| my $copyright; |
| my $spdx; |
| my @examples; # there can be more than one |
| my $magic; # cmdline special option |
| my $line; |
| my $dline; |
| my $multi; |
| my $scope; |
| my $experimental; |
| my $start; |
| my $list; # identifies the list, 1 example, 2 see-also |
| while(<$fh>) { |
| $line++; |
| if(/^ *<!--/) { |
| next; |
| } |
| if(!$start) { |
| if(/^---/) { |
| $start = 1; |
| } |
| next; |
| } |
| if(/^Short: *(.)/i) { |
| $short=$1; |
| } |
| elsif(/^Long: *(.*)/i) { |
| $long=$1; |
| } |
| elsif(/^Added: *(.*)/i) { |
| $added=$1; |
| } |
| elsif(/^Tags: *(.*)/i) { |
| $tags=$1; |
| } |
| elsif(/^Arg: *(.*)/i) { |
| $arg=$1; |
| } |
| elsif(/^Magic: *(.*)/i) { |
| $magic=$1; |
| } |
| elsif(/^Mutexed: *(.*)/i) { |
| $mutexed=$1; |
| } |
| elsif(/^Protocols: *(.*)/i) { |
| $protocols=$1; |
| } |
| elsif(/^See-also: +(.+)/i) { |
| if($seealso) { |
| print STDERR "ERROR: duplicated See-also in $f\n"; |
| return 1; |
| } |
| push @seealso, $1; |
| } |
| elsif(/^See-also:/i) { |
| $list=2; |
| } |
| elsif(/^ *- (.*)/i && ($list == 2)) { |
| push @seealso, $1; |
| } |
| elsif(/^Requires: *(.*)/i) { |
| $requires=$1; |
| } |
| elsif(/^Category: *(.*)/i) { |
| $category=$1; |
| } |
| elsif(/^Example: +(.+)/i) { |
| push @examples, $1; |
| } |
| elsif(/^Example:/i) { |
| # '1' is the example list |
| $list = 1; |
| } |
| elsif(/^ *- (.*)/i && ($list == 1)) { |
| push @examples, $1; |
| } |
| elsif(/^Multi: *(.*)/i) { |
| $multi=$1; |
| } |
| elsif(/^Scope: *(.*)/i) { |
| $scope=$1; |
| } |
| elsif(/^Experimental: yes/i) { |
| $experimental=1; |
| } |
| elsif(/^C: (.*)/i) { |
| $copyright=$1; |
| } |
| elsif(/^SPDX-License-Identifier: (.*)/i) { |
| $spdx=$1; |
| } |
| elsif(/^Help: *(.*)/i) { |
| ; |
| } |
| elsif(/^---/) { |
| $start++; |
| if(!$long) { |
| print STDERR "ERROR: no 'Long:' in $f\n"; |
| return 1; |
| } |
| if(!$category) { |
| print STDERR "ERROR: no 'Category:' in $f\n"; |
| return 2; |
| } |
| if(!$examples[0]) { |
| print STDERR "$f:$line:1:ERROR: no 'Example:' present\n"; |
| return 2; |
| } |
| if(!$added) { |
| print STDERR "$f:$line:1:ERROR: no 'Added:' version present\n"; |
| return 2; |
| } |
| if(!$seealso[0]) { |
| print STDERR "$f:$line:1:ERROR: no 'See-also:' field present\n"; |
| return 2; |
| } |
| if(!$copyright) { |
| print STDERR "$f:$line:1:ERROR: no 'C:' field present\n"; |
| return 2; |
| } |
| if(!$spdx) { |
| print STDERR "$f:$line:1:ERROR: no 'SPDX-License-Identifier:' field present\n"; |
| return 2; |
| } |
| last; |
| } |
| else { |
| chomp; |
| print STDERR "$f:$line:1:WARN: unrecognized line in $f, ignoring:\n:'$_';" |
| } |
| } |
| |
| if($start < 2) { |
| print STDERR "$f:1:1:ERROR: no proper meta-data header\n"; |
| return 2; |
| } |
| |
| my @desc = render($manpage, $fh, $f, $line); |
| close($fh); |
| if($tablemode) { |
| # end of table |
| push @desc, ".RE\n.IP\n"; |
| } |
| my $opt; |
| |
| if(defined($short) && $long) { |
| $opt = "-$short, --$long"; |
| } |
| elsif($short && !$long) { |
| $opt = "-$short"; |
| } |
| elsif($long && !$short) { |
| $opt = "--$long"; |
| } |
| |
| if($arg) { |
| $opt .= " $arg"; |
| } |
| |
| # quote "bare" minuses in opt |
| $opt =~ s/-/\\-/g if($manpage); |
| if($standalone) { |
| print ".TH curl 1 \"30 Nov 2016\" \"curl 7.52.0\" \"curl manual\"\n"; |
| print ".SH OPTION\n"; |
| print "curl $opt\n"; |
| } |
| elsif($manpage) { |
| print ".IP \"$opt\"\n"; |
| } |
| else { |
| lastline(1, $opt); |
| } |
| my @leading; |
| if($protocols) { |
| push @leading, protocols($manpage, $standalone, $protocols); |
| } |
| |
| if($standalone) { |
| print ".SH DESCRIPTION\n"; |
| } |
| |
| if($experimental) { |
| push @leading, "**WARNING**: this option is experimental. Do not use in production.\n\n"; |
| } |
| |
| my $pre = $manpage ? "\n": "[1]"; |
| |
| if($scope) { |
| if($scope eq "global") { |
| push @desc, "\n" if(!$manpage); |
| push @desc, "${pre}This option is global and does not need to be specified for each use of --next.\n"; |
| } |
| else { |
| print STDERR "$f:$line:1:ERROR: unrecognized scope: '$scope'\n"; |
| return 2; |
| } |
| } |
| |
| printdesc($manpage, 2, (@leading, @desc)); |
| undef @desc; |
| |
| my @extra; |
| if($multi eq "single") { |
| push @extra, "${pre}If --$long is provided several times, the last set ". |
| "value is used.\n"; |
| } |
| elsif($multi eq "append") { |
| push @extra, "${pre}--$long can be used several times in a command line\n"; |
| } |
| elsif($multi eq "boolean") { |
| my $rev = "no-$long"; |
| # for options that start with "no-" the reverse is then without |
| # the no- prefix |
| if($long =~ /^no-/) { |
| $rev = $long; |
| $rev =~ s/^no-//; |
| } |
| my $dashes = $manpage ? "\\-\\-" : "--"; |
| push @extra, |
| "${pre}Providing --$long multiple times has no extra effect.\n". |
| "Disable it again with $dashes$rev.\n"; |
| } |
| elsif($multi eq "mutex") { |
| push @extra, |
| "${pre}Providing --$long multiple times has no extra effect.\n"; |
| } |
| elsif($multi eq "custom") { |
| ; # left for the text to describe |
| } |
| else { |
| print STDERR "$f:$line:1:ERROR: unrecognized Multi: '$multi'\n"; |
| return 2; |
| } |
| |
| printdesc($manpage, 2, @extra); |
| |
| my @foot; |
| |
| my $mstr; |
| my $and = 0; |
| my $num = scalar(@seealso); |
| if($num > 2) { |
| # use commas up to this point |
| $and = $num - 1; |
| } |
| my $i = 0; |
| for my $k (@seealso) { |
| if(!$helplong{$k}) { |
| print STDERR "$f:$line:1:WARN: see-also a non-existing option: $k\n"; |
| } |
| my $l = $manpage ? manpageify($k) : "--$k"; |
| my $sep = " and"; |
| if($and && ($i < $and)) { |
| $sep = ","; |
| } |
| $mstr .= sprintf "%s$l", $mstr?"$sep ":""; |
| $i++; |
| } |
| push @foot, seealso($standalone, $mstr); |
| |
| if($requires) { |
| my $l = $manpage ? manpageify($long) : "--$long"; |
| push @foot, "$l requires that the underlying libcurl". |
| " was built to support $requires. "; |
| } |
| if($mutexed) { |
| my @m=split(/ /, $mutexed); |
| my $mstr; |
| for my $k (@m) { |
| if(!$helplong{$k}) { |
| print STDERR "WARN: $f mutexes a non-existing option: $k\n"; |
| } |
| my $l = $manpage ? manpageify($k) : "--$k"; |
| $mstr .= sprintf "%s$l", $mstr?" and ":""; |
| } |
| push @foot, overrides($standalone, |
| "This option is mutually exclusive to $mstr. "); |
| } |
| if($examples[0]) { |
| my $s =""; |
| $s="s" if($examples[1]); |
| if($manpage) { |
| print "\nExample$s:\n"; |
| print ".nf\n"; |
| foreach my $e (@examples) { |
| $e =~ s!\$URL!https://example.com!g; |
| # convert single backslahes to doubles |
| $e =~ s/\\/\\\\/g; |
| print " curl $e\n"; |
| } |
| print ".fi\n"; |
| } |
| else { |
| my @ex; |
| push @ex, "[0q]Example$s:\n"; |
| foreach my $e (@examples) { |
| $e =~ s!\$URL!https://example.com!g; |
| push @ex, "[0q] curl $e\n"; |
| } |
| printdesc($manpage, 2, @ex); |
| } |
| } |
| if($added) { |
| push @foot, added($standalone, $added); |
| } |
| if($foot[0]) { |
| print "\n"; |
| my $f = join("", @foot); |
| if($manpage) { |
| $f =~ s/ +\z//; # remove trailing space |
| print "$f\n"; |
| } |
| else { |
| printdesc($manpage, 2, "[1]$f"); |
| } |
| } |
| return 0; |
| } |
| |
| sub getshortlong { |
| my ($dir, $f)=@_; |
| $f =~ s/^.*\///; |
| open(F, "<:crlf", "$dir/$f") || |
| die "could not find $dir/$f"; |
| my $short; |
| my $long; |
| my $help; |
| my $arg; |
| my $protocols; |
| my $category; |
| my $start = 0; |
| while(<F>) { |
| if(!$start) { |
| if(/^---/) { |
| $start = 1; |
| } |
| next; |
| } |
| if(/^Short: (.)/i) { |
| $short=$1; |
| } |
| elsif(/^Long: (.*)/i) { |
| $long=$1; |
| } |
| elsif(/^Help: (.*)/i) { |
| $help=$1; |
| } |
| elsif(/^Arg: (.*)/i) { |
| $arg=$1; |
| } |
| elsif(/^Protocols: (.*)/i) { |
| $protocols=$1; |
| } |
| elsif(/^Category: (.*)/i) { |
| $category=$1; |
| } |
| elsif(/^---/) { |
| last; |
| } |
| } |
| close(F); |
| if($short) { |
| $optshort{$short}=$long; |
| } |
| if($long) { |
| $optlong{$long}=$short; |
| $helplong{$long}=$help; |
| $arglong{$long}=$arg; |
| $protolong{$long}=$protocols; |
| $catlong{$long}=$category; |
| } |
| } |
| |
| sub indexoptions { |
| my ($dir, @files) = @_; |
| foreach my $f (@files) { |
| getshortlong($dir, $f); |
| } |
| } |
| |
| sub header { |
| my ($dir, $manpage, $f)=@_; |
| my $fh; |
| open($fh, "<:crlf", "$dir/$f") || |
| die "could not find $dir/$f"; |
| my @d = render($manpage, $fh, $f, 1); |
| close($fh); |
| printdesc($manpage, 0, @d); |
| } |
| |
| sub listhelp { |
| print <<HEAD |
| /*************************************************************************** |
| * _ _ ____ _ |
| * Project ___| | | | _ \\| | |
| * / __| | | | |_) | | |
| * | (__| |_| | _ <| |___ |
| * \\___|\\___/|_| \\_\\_____| |
| * |
| * Copyright (C) Daniel Stenberg, <daniel\@haxx.se>, et al. |
| * |
| * This software is licensed as described in the file COPYING, which |
| * you should have received as part of this distribution. The terms |
| * are also available at https://curl.se/docs/copyright.html. |
| * |
| * You may opt to use, copy, modify, merge, publish, distribute and/or sell |
| * copies of the Software, and permit persons to whom the Software is |
| * furnished to do so, under the terms of the COPYING file. |
| * |
| * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
| * KIND, either express or implied. |
| * |
| * SPDX-License-Identifier: curl |
| * |
| ***************************************************************************/ |
| #include "tool_setup.h" |
| #include "tool_help.h" |
| |
| /* |
| * DO NOT edit tool_listhelp.c manually. |
| * This source file is generated with the following command in an autotools |
| * build: |
| * |
| * "make listhelp" |
| */ |
| |
| const struct helptxt helptext[] = { |
| HEAD |
| ; |
| foreach my $f (sort keys %helplong) { |
| my $long = $f; |
| my $short = $optlong{$long}; |
| my @categories = split ' ', $catlong{$long}; |
| my $bitmask = ' '; |
| my $opt; |
| |
| if(defined($short) && $long) { |
| $opt = "-$short, --$long"; |
| } |
| elsif($long && !$short) { |
| $opt = " --$long"; |
| } |
| for my $i (0 .. $#categories) { |
| $bitmask .= 'CURLHELP_' . uc $categories[$i]; |
| # If not last element, append | |
| if($i < $#categories) { |
| $bitmask .= ' | '; |
| } |
| } |
| $bitmask =~ s/(?=.{76}).{1,76}\|/$&\n /g; |
| my $arg = $arglong{$long}; |
| if($arg) { |
| $opt .= " $arg"; |
| } |
| my $desc = $helplong{$f}; |
| $desc =~ s/\"/\\\"/g; # escape double quotes |
| |
| my $line = sprintf " {\"%s\",\n \"%s\",\n %s},\n", $opt, $desc, $bitmask; |
| |
| if(length($opt) > 78) { |
| print STDERR "WARN: the --$long name is too long\n"; |
| } |
| elsif(length($desc) > 78) { |
| print STDERR "WARN: the --$long description is too long\n"; |
| } |
| print $line; |
| } |
| print <<FOOT |
| { NULL, NULL, CURLHELP_HIDDEN } |
| }; |
| FOOT |
| ; |
| } |
| |
| sub listcats { |
| my %allcats; |
| foreach my $f (sort keys %helplong) { |
| my @categories = split ' ', $catlong{$f}; |
| foreach (@categories) { |
| $allcats{$_} = undef; |
| } |
| } |
| my @categories; |
| foreach my $key (keys %allcats) { |
| push @categories, $key; |
| } |
| @categories = sort @categories; |
| unshift @categories, 'hidden'; |
| for my $i (0..$#categories) { |
| print '#define ' . 'CURLHELP_' . uc($categories[$i]) . ' ' . "1u << " . $i . "u\n"; |
| } |
| } |
| |
| sub listglobals { |
| my ($dir, @files) = @_; |
| my @globalopts; |
| |
| # Find all global options and output them |
| foreach my $f (sort @files) { |
| open(F, "<:crlf", "$dir/$f") || |
| die "could not read $dir/$f"; |
| my $long; |
| my $start = 0; |
| while(<F>) { |
| if(/^---/) { |
| if(!$start) { |
| $start = 1; |
| next; |
| } |
| else { |
| last; |
| } |
| } |
| if(/^Long: *(.*)/i) { |
| $long=$1; |
| } |
| elsif(/^Scope: global/i) { |
| push @globalopts, $long; |
| last; |
| } |
| } |
| close(F); |
| } |
| return $ret if($ret); |
| for my $e (0 .. $#globalopts) { |
| $globals .= sprintf "%s--%s", $e?($globalopts[$e+1] ? ", " : " and "):"", |
| $globalopts[$e],; |
| } |
| } |
| |
| sub noext { |
| my $in = $_[0]; |
| $in =~ s/\.md//; |
| return $in; |
| } |
| |
| sub sortnames { |
| return noext($a) cmp noext($b); |
| } |
| |
| sub mainpage { |
| my ($dir, $manpage, @files) = @_; |
| # $manpage is 1 for nroff, 0 for ASCII |
| my $ret; |
| my $fh; |
| open($fh, "<:crlf", "$dir/mainpage.idx") || |
| die "no $dir/mainpage.idx file"; |
| |
| print <<HEADER |
| .\\" ************************************************************************** |
| .\\" * _ _ ____ _ |
| .\\" * Project ___| | | | _ \\| | |
| .\\" * / __| | | | |_) | | |
| .\\" * | (__| |_| | _ <| |___ |
| .\\" * \\___|\\___/|_| \\_\\_____| |
| .\\" * |
| .\\" * Copyright (C) Daniel Stenberg, <daniel\@haxx.se>, et al. |
| .\\" * |
| .\\" * This software is licensed as described in the file COPYING, which |
| .\\" * you should have received as part of this distribution. The terms |
| .\\" * are also available at https://curl.se/docs/copyright.html. |
| .\\" * |
| .\\" * You may opt to use, copy, modify, merge, publish, distribute and/or sell |
| .\\" * copies of the Software, and permit persons to whom the Software is |
| .\\" * furnished to do so, under the terms of the COPYING file. |
| .\\" * |
| .\\" * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
| .\\" * KIND, either express or implied. |
| .\\" * |
| .\\" * SPDX-License-Identifier: curl |
| .\\" * |
| .\\" ************************************************************************** |
| .\\" |
| .\\" DO NOT EDIT. Generated by the curl project managen man page generator. |
| .\\" |
| .TH curl 1 "$date" "curl $version" "curl Manual" |
| HEADER |
| if ($manpage); |
| |
| while(<$fh>) { |
| my $f = $_; |
| chomp $f; |
| if($f =~ /^#/) { |
| # standard comment |
| next; |
| } |
| if(/^%options/) { |
| # output docs for all options |
| foreach my $f (sort sortnames @files) { |
| $ret += single($dir, $manpage, $f, 0); |
| } |
| } |
| else { |
| # render the file |
| header($dir, $manpage, $f); |
| } |
| } |
| close($fh); |
| exit $ret if($ret); |
| } |
| |
| sub showonly { |
| my ($f) = @_; |
| if(single($f, 1)) { |
| print STDERR "$f: failed\n"; |
| } |
| } |
| |
| sub showprotocols { |
| my %prots; |
| foreach my $f (keys %optlong) { |
| my @p = split(/ /, $protolong{$f}); |
| for my $p (@p) { |
| $prots{$p}++; |
| } |
| } |
| for(sort keys %prots) { |
| printf "$_ (%d options)\n", $prots{$_}; |
| } |
| } |
| |
| sub getargs { |
| my ($dir, $f, @s) = @_; |
| if($f eq "mainpage") { |
| listglobals($dir, @s); |
| mainpage($dir, 1, @s); |
| return; |
| } |
| elsif($f eq "ascii") { |
| listglobals($dir, @s); |
| mainpage($dir, 0, @s); |
| return; |
| } |
| elsif($f eq "listhelp") { |
| listhelp(); |
| return; |
| } |
| elsif($f eq "single") { |
| showonly($s[0]); |
| return; |
| } |
| elsif($f eq "protos") { |
| showprotocols(); |
| return; |
| } |
| elsif($f eq "listcats") { |
| listcats(); |
| return; |
| } |
| |
| print "Usage: managen ". |
| "[-d dir] <mainpage/ascii/listhelp/single FILE/protos/listcats> [files]\n"; |
| } |
| |
| #------------------------------------------------------------------------ |
| |
| my $dir = "."; |
| my $include = "../../include"; |
| my $cmd = shift @ARGV; |
| |
| check: |
| if($cmd eq "-d") { |
| # specifies source directory |
| $dir = shift @ARGV; |
| $cmd = shift @ARGV; |
| goto check; |
| } |
| elsif($cmd eq "-I") { |
| # include path root |
| $include = shift @ARGV; |
| $cmd = shift @ARGV; |
| goto check; |
| } |
| |
| my @files = @ARGV; # the rest are the files |
| |
| open(INC, "<$include/curl/curlver.h"); |
| while(<INC>) { |
| if($_ =~ /^#define LIBCURL_VERSION \"([0-9.]*)/) { |
| $version = $1; |
| last; |
| } |
| } |
| close(INC); |
| |
| # learn all existing options |
| indexoptions($dir, @files); |
| |
| getargs($dir, $cmd, @files); |