aboutsummaryrefslogtreecommitdiff
path: root/docs/cmdline-opts/gen.pl
diff options
context:
space:
mode:
Diffstat (limited to 'docs/cmdline-opts/gen.pl')
-rwxr-xr-xdocs/cmdline-opts/gen.pl431
1 files changed, 326 insertions, 105 deletions
diff --git a/docs/cmdline-opts/gen.pl b/docs/cmdline-opts/gen.pl
index 8b9b98bb2..f4dcce8ae 100755
--- a/docs/cmdline-opts/gen.pl
+++ b/docs/cmdline-opts/gen.pl
@@ -48,8 +48,14 @@ my %protolong;
my %catlong;
use POSIX qw(strftime);
-my $date = strftime "%B %d %Y", localtime;
-my $year = strftime "%Y", localtime;
+my @ts;
+if (defined($ENV{SOURCE_DATE_EPOCH})) {
+ @ts = localtime($ENV{SOURCE_DATE_EPOCH});
+} else {
+ @ts = localtime;
+}
+my $date = strftime "%B %d %Y", @ts;
+my $year = strftime "%Y", @ts;
my $version = "unknown";
my $globals;
@@ -84,52 +90,8 @@ sub printdesc {
my @desc = @_;
my $exam = 0;
for my $d (@desc) {
- if($d =~ /\(Added in ([0-9.]+)\)/i) {
- my $ver = $1;
- if(too_old($ver)) {
- $d =~ s/ *\(Added in $ver\)//gi;
- }
- }
- if($d !~ /^.\\"/) {
- # **bold**
- $d =~ s/\*\*([^ ]*)\*\*/\\fB$1\\fP/g;
- # *italics*
- $d =~ s/\*([^ ]*)\*/\\fI$1\\fP/g;
- }
- if(!$exam && ($d =~ /^ /)) {
- # start of example
- $exam = 1;
- print ".nf\n"; # no-fill
- }
- elsif($exam && ($d !~ /^ /)) {
- # end of example
- $exam = 0;
- print ".fi\n"; # fill-in
- }
- # skip lines starting with space (examples)
- if($d =~ /^[^ ]/ && $d =~ /--/) {
- # scan for options in longest-names first order
- for my $k (sort {length($b) <=> length($a)} keys %optlong) {
- # --tlsv1 is complicated since --tlsv1.2 etc are also
- # acceptable options!
- if(($k eq "tlsv1") && ($d =~ /--tlsv1\.[0-9]\\f/)) {
- next;
- }
- my $l = manpageify($k);
- $d =~ s/\-\-$k([^a-z0-9-])/$l$1/g;
- }
- }
- # quote minuses in the output
- $d =~ s/([^\\])-/$1\\-/g;
- # replace single quotes
- $d =~ s/\'/\\(aq/g;
- # handle double quotes first on the line
- $d =~ s/^(\s*)\"/$1\\(dq/;
print $d;
}
- if($exam) {
- print ".fi\n"; # fill-in
- }
}
sub seealso {
@@ -194,9 +156,173 @@ sub added {
}
}
+sub render {
+ my ($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;
+ $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";
+ 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
+ if($word =~ / /) {
+ $word = "\"$word\"";
+ }
+ if($top == 1) {
+ push @desc, ".IP $word\n";
+ }
+ else {
+ if(!$tablemode) {
+ push @desc, ".RS\n";
+ $tablemode = 1;
+ }
+ push @desc, ".IP $word\n";
+ }
+ $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";
+ $tablmode = 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) {
+ push @desc, ".nf\n";
+ }
+ $quote = 1;
+ $d = "$word\n";
+ }
+ elsif($quote && ($d !~ /^ (.*)/)) {
+ # end of quote
+ push @desc, ".fi\n";
+ $quote = 0;
+ }
+
+ $d =~ s/`%DATE`/$date/g;
+ $d =~ s/`%VERSION`/$version/g;
+ $d =~ s/`%GLOBALS`/$globals/g;
+
+ # convert single backslahes to doubles
+ $d =~ s/\\/\\\\/g;
+
+ # convert backticks to double quotes
+ $d =~ s/\`/\"/g;
+
+ if(!$quote && $d =~ /--/) {
+ # scan for options in longest-names first order
+ for my $k (sort {length($b) <=> length($a)} keys %optlong) {
+ # --tlsv1 is complicated since --tlsv1.2 etc are also
+ # acceptable options!
+ if(($k eq "tlsv1") && ($d =~ /--tlsv1\.[0-9]\\f/)) {
+ next;
+ }
+ my $l = manpageify($k);
+ $d =~ s/\-\-$k([^a-z0-9-])/$l$1/g;
+ }
+ }
+
+ if($d =~ /\(Added in ([0-9.]+)\)/i) {
+ my $ver = $1;
+ if(too_old($ver)) {
+ $d =~ s/ *\(Added in $ver\)//gi;
+ }
+ }
+
+ if(!$quote && ($d =~ /^(.*) /)) {
+ printf STDERR "$f:$line:%d:ERROR: 2 spaces detected\n",
+ length($1);
+ return 3;
+ }
+ # 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;
+
+ # trim trailing spaces
+ $d =~ s/[ \t]+\z//;
+ push @desc, "\n" if($blankline && !$header);
+ $blankline = 0;
+ push @desc, $d;
+ $header = 0;
+
+ }
+ if($tablemode) {
+ # end of table
+ push @desc, ".RE\n.IP\n";
+ }
+ return @desc;
+}
+
sub single {
my ($f, $standalone)=@_;
- open(F, "<:crlf", "$f") ||
+ my $fh;
+ open($fh, "<:crlf", "$f") ||
return 1;
my $short;
my $long;
@@ -207,17 +333,29 @@ sub single {
my $mutexed;
my $requires;
my $category;
- my $seealso;
+ 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;
- while(<F>) {
+ 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;
}
@@ -242,12 +380,18 @@ sub single {
elsif(/^Protocols: *(.*)/i) {
$protocols=$1;
}
- elsif(/^See-also: *(.*)/i) {
+ elsif(/^See-also: +(.+)/i) {
if($seealso) {
print STDERR "ERROR: duplicated See-also in $f\n";
return 1;
}
- $seealso=$1;
+ push @seealso, $1;
+ }
+ elsif(/^See-also:/i) {
+ $list=2;
+ }
+ elsif(/^ *- (.*)/i && ($list == 2)) {
+ push @seealso, $1;
}
elsif(/^Requires: *(.*)/i) {
$requires=$1;
@@ -255,7 +399,14 @@ sub single {
elsif(/^Category: *(.*)/i) {
$category=$1;
}
- elsif(/^Example: *(.*)/i) {
+ 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) {
@@ -277,6 +428,7 @@ sub single {
;
}
elsif(/^---/) {
+ $start++;
if(!$long) {
print STDERR "ERROR: no 'Long:' in $f\n";
return 1;
@@ -293,7 +445,7 @@ sub single {
print STDERR "$f:$line:1:ERROR: no 'Added:' version present\n";
return 2;
}
- if(!$seealso) {
+ if(!$seealso[0]) {
print STDERR "$f:$line:1:ERROR: no 'See-also:' field present\n";
return 2;
}
@@ -309,14 +461,21 @@ sub single {
}
else {
chomp;
- print STDERR "WARN: unrecognized line in $f, ignoring:\n:'$_';"
+ print STDERR "$f:$line:1:WARN: unrecognized line in $f, ignoring:\n:'$_';"
}
}
- my @desc;
- while(<F>) {
- push @desc, $_;
+
+ if($start < 2) {
+ print STDERR "$f:1:1:ERROR: no proper meta-data header\n";
+ return 2;
+ }
+
+ my @desc = render($fh, $f, $line);
+ close($fh);
+ if($tablemode) {
+ # end of table
+ push @desc, ".RE\n.IP\n";
}
- close(F);
my $opt;
if(defined($short) && $long) {
@@ -403,30 +562,28 @@ sub single {
printdesc(@extra);
my @foot;
- if($seealso) {
- my @m=split(/ /, $seealso);
- my $mstr;
- my $and = 0;
- my $num = scalar(@m);
- if($num > 2) {
- # use commas up to this point
- $and = $num - 1;
+
+ 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 $i = 0;
- for my $k (@m) {
- if(!$helplong{$k}) {
- print STDERR "$f:$line:1:WARN: see-also a non-existing option: $k\n";
- }
- my $l = manpageify($k);
- my $sep = " and";
- if($and && ($i < $and)) {
- $sep = ",";
- }
- $mstr .= sprintf "%s$l", $mstr?"$sep ":"";
- $i++;
+ my $l = manpageify($k);
+ my $sep = " and";
+ if($and && ($i < $and)) {
+ $sep = ",";
}
- push @foot, seealso($standalone, $mstr);
+ $mstr .= sprintf "%s$l", $mstr?"$sep ":"";
+ $i++;
}
+ push @foot, seealso($standalone, $mstr);
if($requires) {
my $l = manpageify($long);
@@ -452,8 +609,10 @@ sub single {
print "\nExample$s:\n.nf\n";
foreach my $e (@examples) {
$e =~ s!\$URL!https://example.com!g;
- $e =~ s/-/\\-/g;
- $e =~ s/\'/\\(aq/g;
+ #$e =~ s/-/\\-/g;
+ #$e =~ s/\'/\\(aq/g;
+ # convert single backslahes to doubles
+ $e =~ s/\\/\\\\/g;
print " curl $e\n";
}
print ".fi\n";
@@ -479,7 +638,14 @@ sub getshortlong {
my $arg;
my $protocols;
my $category;
+ my $start = 0;
while(<F>) {
+ if(!$start) {
+ if(/^---/) {
+ $start = 1;
+ }
+ next;
+ }
if(/^Short: (.)/i) {
$short=$1;
}
@@ -524,15 +690,10 @@ sub indexoptions {
sub header {
my ($f)=@_;
- open(F, "<:crlf", "$f");
- my @d;
- while(<F>) {
- s/%DATE/$date/g;
- s/%VERSION/$version/g;
- s/%GLOBALS/$globals/g;
- push @d, $_;
- }
- close(F);
+ my $fh;
+ open($fh, "<:crlf", "$f");
+ my @d = render($fh, $f, 1);
+ close($fh);
printdesc(@d);
}
@@ -566,10 +727,10 @@ sub listhelp {
/*
* DO NOT edit tool_listhelp.c manually.
- * This source file is generated with the following command:
-
- cd \$srcroot/docs/cmdline-opts
- ./gen.pl listhelp *.d > \$srcroot/src/tool_listhelp.c
+ * This source file is generated with the following command in an autotools
+ * build:
+ *
+ * "make listhelp"
*/
const struct helptxt helptext[] = {
@@ -648,7 +809,17 @@ sub listglobals {
open(F, "<:crlf", "$f") ||
next;
my $long;
+ my $start = 0;
while(<F>) {
+ if(/^---/) {
+ if(!$start) {
+ $start = 1;
+ next;
+ }
+ else {
+ last;
+ }
+ }
if(/^Long: *(.*)/i) {
$long=$1;
}
@@ -656,9 +827,6 @@ sub listglobals {
push @globalopts, $long;
last;
}
- elsif(/^---/) {
- last;
- }
}
close(F);
}
@@ -669,20 +837,73 @@ sub listglobals {
}
}
+sub noext {
+ my $in = $_[0];
+ $in =~ s/\.d//;
+ return $in;
+}
+
+sub sortnames {
+ return noext($a) cmp noext($b);
+}
+
sub mainpage {
my (@files) = @_;
my $ret;
- # show the page header
- header("page-header");
+ my $fh;
+ open($fh, "<:crlf", "mainpage.idx") ||
+ return 1;
- # output docs for all options
- foreach my $f (sort @files) {
- $ret += single($f, 0);
- }
+ 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 gen.pl man page generator.
+.\\"
+.TH curl 1 "$date" "curl $version" "curl Manual"
+HEADER
+ ;
- if(!$ret) {
- header("page-footer");
+ while(<$fh>) {
+ my $f = $_;
+ chomp $f;
+ if($f =~ /^#/) {
+ # stardard comment
+ next;
+ }
+ if(/^%options/) {
+ # output docs for all options
+ foreach my $f (sort sortnames @files) {
+ $ret += single($f, 0);
+ }
+ }
+ else {
+ # render the file
+ header($f);
+ }
}
+ close($fh);
exit $ret if($ret);
}