#!/usr/bin/env perl #*************************************************************************** # _ _ ____ _ # Project ___| | | | _ \| | # / __| | | | |_) | | # | (__| |_| | _ <| |___ # \___|\___/|_| \_\_____| # # Copyright (C) Daniel Stenberg, , 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 [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; my $error = 0; my $indent = 4; # get the long name version, return the manpage 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=79; # max number of columns sub prefixline { my ($num) = @_; print "\t" x ($num/8); print ' ' x ($num%8); } sub justline { my ($lvl, @line) = @_; my $w = -1; my $spaces = -1; my $width = $colwidth - ($lvl * $indent); 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; prefixline($lvl * $indent); 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) = @_; $line[0] =~ s/^( +)//; prefixline($lvl * $indent + length($1)); 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 * $indent); 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); my $w = ($baselvl + $lvl + 1) * $indent + length($l); if ($w > $colwidth) { print STDERR "ERROR: $w columns is too long\n"; print STDERR "$l\n"; $error++; } } 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 < 7600) { # we consider everything before 7.60.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; my $finalblank; $start = 0; while(<$fh>) { my $d = $_; $line++; $finalblank = ($d eq "\n"); if($d =~ /^\.(SH|BR|IP|B)/) { print STDERR "$f:$line:1:ERROR: nroff instruction in input: \".$1\"\n"; return 4; } if(/^ *