113498266Sopenharmony_ci#!/usr/bin/env perl 213498266Sopenharmony_ci#*************************************************************************** 313498266Sopenharmony_ci# _ _ ____ _ 413498266Sopenharmony_ci# Project ___| | | | _ \| | 513498266Sopenharmony_ci# / __| | | | |_) | | 613498266Sopenharmony_ci# | (__| |_| | _ <| |___ 713498266Sopenharmony_ci# \___|\___/|_| \_\_____| 813498266Sopenharmony_ci# 913498266Sopenharmony_ci# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 1013498266Sopenharmony_ci# 1113498266Sopenharmony_ci# This software is licensed as described in the file COPYING, which 1213498266Sopenharmony_ci# you should have received as part of this distribution. The terms 1313498266Sopenharmony_ci# are also available at https://curl.se/docs/copyright.html. 1413498266Sopenharmony_ci# 1513498266Sopenharmony_ci# You may opt to use, copy, modify, merge, publish, distribute and/or sell 1613498266Sopenharmony_ci# copies of the Software, and permit persons to whom the Software is 1713498266Sopenharmony_ci# furnished to do so, under the terms of the COPYING file. 1813498266Sopenharmony_ci# 1913498266Sopenharmony_ci# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 2013498266Sopenharmony_ci# KIND, either express or implied. 2113498266Sopenharmony_ci# 2213498266Sopenharmony_ci# SPDX-License-Identifier: curl 2313498266Sopenharmony_ci# 2413498266Sopenharmony_ci########################################################################### 2513498266Sopenharmony_ci 2613498266Sopenharmony_ci=begin comment 2713498266Sopenharmony_ci 2813498266Sopenharmony_ciConverts a curldown file to nroff (man page). 2913498266Sopenharmony_ci 3013498266Sopenharmony_ci=end comment 3113498266Sopenharmony_ci=cut 3213498266Sopenharmony_ci 3313498266Sopenharmony_ciuse strict; 3413498266Sopenharmony_ciuse warnings; 3513498266Sopenharmony_ci 3613498266Sopenharmony_cimy $cd2nroff = "0.1"; # to keep check 3713498266Sopenharmony_cimy $dir; 3813498266Sopenharmony_cimy $extension; 3913498266Sopenharmony_cimy $keepfilename; 4013498266Sopenharmony_ci 4113498266Sopenharmony_ciwhile(@ARGV) { 4213498266Sopenharmony_ci if($ARGV[0] eq "-d") { 4313498266Sopenharmony_ci shift @ARGV; 4413498266Sopenharmony_ci $dir = shift @ARGV; 4513498266Sopenharmony_ci } 4613498266Sopenharmony_ci elsif($ARGV[0] eq "-e") { 4713498266Sopenharmony_ci shift @ARGV; 4813498266Sopenharmony_ci $extension = shift @ARGV; 4913498266Sopenharmony_ci } 5013498266Sopenharmony_ci elsif($ARGV[0] eq "-k") { 5113498266Sopenharmony_ci shift @ARGV; 5213498266Sopenharmony_ci $keepfilename = 1; 5313498266Sopenharmony_ci } 5413498266Sopenharmony_ci elsif($ARGV[0] eq "-h") { 5513498266Sopenharmony_ci print <<HELP 5613498266Sopenharmony_ciUsage: cd2nroff [options] [file.md] 5713498266Sopenharmony_ci 5813498266Sopenharmony_ci-d <dir> Write the output to the file name from the meta-data in the 5913498266Sopenharmony_ci specified directory, instead of writing to stdout 6013498266Sopenharmony_ci-e <ext> If -d is used, this option can provide an added "extension", arbitrary 6113498266Sopenharmony_ci text really, to append to the file name. 6213498266Sopenharmony_ci-h This help text, 6313498266Sopenharmony_ci-v Show version then exit 6413498266Sopenharmony_ciHELP 6513498266Sopenharmony_ci ; 6613498266Sopenharmony_ci exit 0; 6713498266Sopenharmony_ci } 6813498266Sopenharmony_ci elsif($ARGV[0] eq "-v") { 6913498266Sopenharmony_ci print "cd2nroff version $cd2nroff\n"; 7013498266Sopenharmony_ci exit 0; 7113498266Sopenharmony_ci } 7213498266Sopenharmony_ci else { 7313498266Sopenharmony_ci last; 7413498266Sopenharmony_ci } 7513498266Sopenharmony_ci} 7613498266Sopenharmony_ci 7713498266Sopenharmony_ciuse POSIX qw(strftime); 7813498266Sopenharmony_cimy @ts; 7913498266Sopenharmony_ciif (defined($ENV{SOURCE_DATE_EPOCH})) { 8013498266Sopenharmony_ci @ts = localtime($ENV{SOURCE_DATE_EPOCH}); 8113498266Sopenharmony_ci} else { 8213498266Sopenharmony_ci @ts = localtime; 8313498266Sopenharmony_ci} 8413498266Sopenharmony_cimy $date = strftime "%B %d %Y", @ts; 8513498266Sopenharmony_ci 8613498266Sopenharmony_cisub outseealso { 8713498266Sopenharmony_ci my (@sa) = @_; 8813498266Sopenharmony_ci my $comma = 0; 8913498266Sopenharmony_ci my @o; 9013498266Sopenharmony_ci push @o, ".SH SEE ALSO\n"; 9113498266Sopenharmony_ci for my $s (sort @sa) { 9213498266Sopenharmony_ci push @o, sprintf "%s.BR $s", $comma ? ",\n": ""; 9313498266Sopenharmony_ci $comma = 1; 9413498266Sopenharmony_ci } 9513498266Sopenharmony_ci push @o, "\n"; 9613498266Sopenharmony_ci return @o; 9713498266Sopenharmony_ci} 9813498266Sopenharmony_ci 9913498266Sopenharmony_cisub single { 10013498266Sopenharmony_ci my @seealso; 10113498266Sopenharmony_ci my $d; 10213498266Sopenharmony_ci my ($f)=@_; 10313498266Sopenharmony_ci my $copyright; 10413498266Sopenharmony_ci my $errors = 0; 10513498266Sopenharmony_ci my $fh; 10613498266Sopenharmony_ci my $line; 10713498266Sopenharmony_ci my $salist; 10813498266Sopenharmony_ci my $section; 10913498266Sopenharmony_ci my $source; 11013498266Sopenharmony_ci my $spdx; 11113498266Sopenharmony_ci my $start = 0; 11213498266Sopenharmony_ci my $title; 11313498266Sopenharmony_ci 11413498266Sopenharmony_ci if(defined($f)) { 11513498266Sopenharmony_ci if(!open($fh, "<:crlf", "$f")) { 11613498266Sopenharmony_ci print STDERR "Failed to open $f : $!\n"; 11713498266Sopenharmony_ci return 1; 11813498266Sopenharmony_ci } 11913498266Sopenharmony_ci } 12013498266Sopenharmony_ci else { 12113498266Sopenharmony_ci $f = "STDIN"; 12213498266Sopenharmony_ci $fh = \*STDIN; 12313498266Sopenharmony_ci binmode($fh, ":crlf"); 12413498266Sopenharmony_ci } 12513498266Sopenharmony_ci while(<$fh>) { 12613498266Sopenharmony_ci $line++; 12713498266Sopenharmony_ci if(!$start) { 12813498266Sopenharmony_ci if(/^---/) { 12913498266Sopenharmony_ci # header starts here 13013498266Sopenharmony_ci $start = 1; 13113498266Sopenharmony_ci } 13213498266Sopenharmony_ci next; 13313498266Sopenharmony_ci } 13413498266Sopenharmony_ci if(/^Title: *(.*)/i) { 13513498266Sopenharmony_ci $title=$1; 13613498266Sopenharmony_ci } 13713498266Sopenharmony_ci elsif(/^Section: *(.*)/i) { 13813498266Sopenharmony_ci $section=$1; 13913498266Sopenharmony_ci } 14013498266Sopenharmony_ci elsif(/^Source: *(.*)/i) { 14113498266Sopenharmony_ci $source=$1; 14213498266Sopenharmony_ci } 14313498266Sopenharmony_ci elsif(/^See-also: +(.*)/i) { 14413498266Sopenharmony_ci $salist = 0; 14513498266Sopenharmony_ci push @seealso, $1; 14613498266Sopenharmony_ci } 14713498266Sopenharmony_ci elsif(/^See-also: */i) { 14813498266Sopenharmony_ci if($seealso[0]) { 14913498266Sopenharmony_ci print STDERR "$f:$line:1:ERROR: bad See-Also, needs list\n"; 15013498266Sopenharmony_ci return 2; 15113498266Sopenharmony_ci } 15213498266Sopenharmony_ci $salist = 1; 15313498266Sopenharmony_ci } 15413498266Sopenharmony_ci elsif(/^ +- (.*)/i) { 15513498266Sopenharmony_ci # the only list we support is the see-also 15613498266Sopenharmony_ci if($salist) { 15713498266Sopenharmony_ci push @seealso, $1; 15813498266Sopenharmony_ci } 15913498266Sopenharmony_ci } 16013498266Sopenharmony_ci # REUSE-IgnoreStart 16113498266Sopenharmony_ci elsif(/^C: (.*)/i) { 16213498266Sopenharmony_ci $copyright=$1; 16313498266Sopenharmony_ci } 16413498266Sopenharmony_ci elsif(/^SPDX-License-Identifier: (.*)/i) { 16513498266Sopenharmony_ci $spdx=$1; 16613498266Sopenharmony_ci } 16713498266Sopenharmony_ci # REUSE-IgnoreEnd 16813498266Sopenharmony_ci elsif(/^---/) { 16913498266Sopenharmony_ci # end of the header section 17013498266Sopenharmony_ci if(!$title) { 17113498266Sopenharmony_ci print STDERR "ERROR: no 'Title:' in $f\n"; 17213498266Sopenharmony_ci return 1; 17313498266Sopenharmony_ci } 17413498266Sopenharmony_ci if(!$section) { 17513498266Sopenharmony_ci print STDERR "ERROR: no 'Section:' in $f\n"; 17613498266Sopenharmony_ci return 2; 17713498266Sopenharmony_ci } 17813498266Sopenharmony_ci if(!$seealso[0]) { 17913498266Sopenharmony_ci print STDERR "$f:$line:1:ERROR: no 'See-also:' present\n"; 18013498266Sopenharmony_ci return 2; 18113498266Sopenharmony_ci } 18213498266Sopenharmony_ci if(!$copyright) { 18313498266Sopenharmony_ci print STDERR "$f:$line:1:ERROR: no 'C:' field present\n"; 18413498266Sopenharmony_ci return 2; 18513498266Sopenharmony_ci } 18613498266Sopenharmony_ci if(!$spdx) { 18713498266Sopenharmony_ci print STDERR "$f:$line:1:ERROR: no 'SPDX-License-Identifier:' field present\n"; 18813498266Sopenharmony_ci return 2; 18913498266Sopenharmony_ci } 19013498266Sopenharmony_ci last; 19113498266Sopenharmony_ci } 19213498266Sopenharmony_ci else { 19313498266Sopenharmony_ci chomp; 19413498266Sopenharmony_ci print STDERR "WARN: unrecognized line in $f, ignoring:\n:'$_';" 19513498266Sopenharmony_ci } 19613498266Sopenharmony_ci } 19713498266Sopenharmony_ci 19813498266Sopenharmony_ci if(!$start) { 19913498266Sopenharmony_ci print STDERR "$f:$line:1:ERROR: no header present\n"; 20013498266Sopenharmony_ci return 2; 20113498266Sopenharmony_ci } 20213498266Sopenharmony_ci 20313498266Sopenharmony_ci my @desc; 20413498266Sopenharmony_ci my $quote = 0; 20513498266Sopenharmony_ci my $blankline = 0; 20613498266Sopenharmony_ci my $header = 0; 20713498266Sopenharmony_ci 20813498266Sopenharmony_ci # cut off the leading path from the file name, if any 20913498266Sopenharmony_ci $f =~ s/^(.*[\\\/])//; 21013498266Sopenharmony_ci 21113498266Sopenharmony_ci push @desc, ".\\\" generated by cd2nroff $cd2nroff from $f\n"; 21213498266Sopenharmony_ci push @desc, ".TH $title $section \"$date\" $source\n"; 21313498266Sopenharmony_ci while(<$fh>) { 21413498266Sopenharmony_ci $line++; 21513498266Sopenharmony_ci 21613498266Sopenharmony_ci $d = $_; 21713498266Sopenharmony_ci 21813498266Sopenharmony_ci if($quote) { 21913498266Sopenharmony_ci if($quote == 4) { 22013498266Sopenharmony_ci # remove the indentation 22113498266Sopenharmony_ci if($d =~ /^ (.*)/) { 22213498266Sopenharmony_ci push @desc, "$1\n"; 22313498266Sopenharmony_ci next; 22413498266Sopenharmony_ci } 22513498266Sopenharmony_ci else { 22613498266Sopenharmony_ci # end of quote 22713498266Sopenharmony_ci $quote = 0; 22813498266Sopenharmony_ci push @desc, ".fi\n"; 22913498266Sopenharmony_ci next; 23013498266Sopenharmony_ci } 23113498266Sopenharmony_ci } 23213498266Sopenharmony_ci if(/^~~~/) { 23313498266Sopenharmony_ci # end of quote 23413498266Sopenharmony_ci $quote = 0; 23513498266Sopenharmony_ci push @desc, ".fi\n"; 23613498266Sopenharmony_ci next; 23713498266Sopenharmony_ci } 23813498266Sopenharmony_ci # convert single backslahes to doubles 23913498266Sopenharmony_ci $d =~ s/\\/\\\\/g; 24013498266Sopenharmony_ci # lines starting with a period needs it escaped 24113498266Sopenharmony_ci $d =~ s/^\./\\&./; 24213498266Sopenharmony_ci push @desc, $d; 24313498266Sopenharmony_ci next; 24413498266Sopenharmony_ci } 24513498266Sopenharmony_ci 24613498266Sopenharmony_ci # **bold** 24713498266Sopenharmony_ci $d =~ s/\*\*(\S.*?)\*\*/\\fB$1\\fP/g; 24813498266Sopenharmony_ci # *italics* 24913498266Sopenharmony_ci $d =~ s/\*(\S.*?)\*/\\fI$1\\fP/g; 25013498266Sopenharmony_ci 25113498266Sopenharmony_ci # mentions of curl symbols with man pages use italics by default 25213498266Sopenharmony_ci $d =~ s/((lib|)curl([^ ]*\(3\)))/\\fI$1\\fP/gi; 25313498266Sopenharmony_ci 25413498266Sopenharmony_ci # backticked becomes italics 25513498266Sopenharmony_ci $d =~ s/\`(.*?)\`/\\fI$1\\fP/g; 25613498266Sopenharmony_ci 25713498266Sopenharmony_ci if(/^## (.*)/) { 25813498266Sopenharmony_ci my $word = $1; 25913498266Sopenharmony_ci # if there are enclosing quotes, remove them first 26013498266Sopenharmony_ci $word =~ s/[\"\'](.*)[\"\']\z/$1/; 26113498266Sopenharmony_ci 26213498266Sopenharmony_ci # enclose in double quotes if there is a space present 26313498266Sopenharmony_ci if($word =~ / /) { 26413498266Sopenharmony_ci push @desc, ".IP \"$word\"\n"; 26513498266Sopenharmony_ci } 26613498266Sopenharmony_ci else { 26713498266Sopenharmony_ci push @desc, ".IP $word\n"; 26813498266Sopenharmony_ci } 26913498266Sopenharmony_ci $header = 1; 27013498266Sopenharmony_ci } 27113498266Sopenharmony_ci elsif(/^# (.*)/) { 27213498266Sopenharmony_ci my $word = $1; 27313498266Sopenharmony_ci # if there are enclosing quotes, remove them first 27413498266Sopenharmony_ci $word =~ s/[\"\'](.*)[\"\']\z/$1/; 27513498266Sopenharmony_ci push @desc, ".SH $word\n"; 27613498266Sopenharmony_ci $header = 1; 27713498266Sopenharmony_ci } 27813498266Sopenharmony_ci elsif(/^~~~c/) { 27913498266Sopenharmony_ci # start of a code section, not indented 28013498266Sopenharmony_ci $quote = 1; 28113498266Sopenharmony_ci push @desc, "\n" if($blankline && !$header); 28213498266Sopenharmony_ci $header = 0; 28313498266Sopenharmony_ci push @desc, ".nf\n"; 28413498266Sopenharmony_ci } 28513498266Sopenharmony_ci elsif(/^~~~/) { 28613498266Sopenharmony_ci # start of a quote section; not code, not indented 28713498266Sopenharmony_ci $quote = 1; 28813498266Sopenharmony_ci push @desc, "\n" if($blankline && !$header); 28913498266Sopenharmony_ci $header = 0; 29013498266Sopenharmony_ci push @desc, ".nf\n"; 29113498266Sopenharmony_ci } 29213498266Sopenharmony_ci elsif(/^ (.*)/) { 29313498266Sopenharmony_ci # quoted, indented by 4 space 29413498266Sopenharmony_ci $quote = 4; 29513498266Sopenharmony_ci push @desc, "\n" if($blankline && !$header); 29613498266Sopenharmony_ci $header = 0; 29713498266Sopenharmony_ci push @desc, ".nf\n$1\n"; 29813498266Sopenharmony_ci } 29913498266Sopenharmony_ci elsif(/^[ \t]*\n/) { 30013498266Sopenharmony_ci # count and ignore blank lines 30113498266Sopenharmony_ci $blankline++; 30213498266Sopenharmony_ci } 30313498266Sopenharmony_ci else { 30413498266Sopenharmony_ci # don't output newlines if this is the first content after a 30513498266Sopenharmony_ci # header 30613498266Sopenharmony_ci push @desc, "\n" if($blankline && !$header); 30713498266Sopenharmony_ci $blankline = 0; 30813498266Sopenharmony_ci $header = 0; 30913498266Sopenharmony_ci 31013498266Sopenharmony_ci # remove single line HTML comments 31113498266Sopenharmony_ci $d =~ s/<!--.*?-->//g; 31213498266Sopenharmony_ci 31313498266Sopenharmony_ci # quote minuses in the output 31413498266Sopenharmony_ci $d =~ s/([^\\])-/$1\\-/g; 31513498266Sopenharmony_ci # replace single quotes 31613498266Sopenharmony_ci $d =~ s/\'/\\(aq/g; 31713498266Sopenharmony_ci # handle double quotes first on the line 31813498266Sopenharmony_ci $d =~ s/^(\s*)\"/$1\\&\"/; 31913498266Sopenharmony_ci 32013498266Sopenharmony_ci # lines starting with a period needs it escaped 32113498266Sopenharmony_ci $d =~ s/^\./\\&./; 32213498266Sopenharmony_ci 32313498266Sopenharmony_ci if($d =~ /^(.*) /) { 32413498266Sopenharmony_ci printf STDERR "$f:$line:%d:ERROR: 2 spaces detected\n", 32513498266Sopenharmony_ci length($1); 32613498266Sopenharmony_ci $errors++; 32713498266Sopenharmony_ci } 32813498266Sopenharmony_ci if($d =~ /^[ \t]*\n/) { 32913498266Sopenharmony_ci # replaced away all contents 33013498266Sopenharmony_ci $blankline= 1; 33113498266Sopenharmony_ci } 33213498266Sopenharmony_ci else { 33313498266Sopenharmony_ci push @desc, $d; 33413498266Sopenharmony_ci } 33513498266Sopenharmony_ci } 33613498266Sopenharmony_ci } 33713498266Sopenharmony_ci if($fh != \*STDIN) { 33813498266Sopenharmony_ci close($fh); 33913498266Sopenharmony_ci } 34013498266Sopenharmony_ci push @desc, outseealso(@seealso); 34113498266Sopenharmony_ci if($dir) { 34213498266Sopenharmony_ci if($keepfilename) { 34313498266Sopenharmony_ci $title = $f; 34413498266Sopenharmony_ci $title =~ s/\.[^.]*$//; 34513498266Sopenharmony_ci } 34613498266Sopenharmony_ci my $outfile = "$dir/$title.$section"; 34713498266Sopenharmony_ci if(defined($extension)) { 34813498266Sopenharmony_ci $outfile .= $extension; 34913498266Sopenharmony_ci } 35013498266Sopenharmony_ci if(!open(O, ">", $outfile)) { 35113498266Sopenharmony_ci print STDERR "Failed to open $outfile : $!\n"; 35213498266Sopenharmony_ci return 1; 35313498266Sopenharmony_ci } 35413498266Sopenharmony_ci print O @desc; 35513498266Sopenharmony_ci close(O); 35613498266Sopenharmony_ci } 35713498266Sopenharmony_ci else { 35813498266Sopenharmony_ci print @desc; 35913498266Sopenharmony_ci } 36013498266Sopenharmony_ci return $errors; 36113498266Sopenharmony_ci} 36213498266Sopenharmony_ci 36313498266Sopenharmony_ciif(@ARGV) { 36413498266Sopenharmony_ci for my $f (@ARGV) { 36513498266Sopenharmony_ci my $r = single($f); 36613498266Sopenharmony_ci if($r) { 36713498266Sopenharmony_ci exit $r; 36813498266Sopenharmony_ci } 36913498266Sopenharmony_ci } 37013498266Sopenharmony_ci} 37113498266Sopenharmony_cielse { 37213498266Sopenharmony_ci exit single(); 37313498266Sopenharmony_ci} 374