diff options
author | Norbert Preining <norbert@preining.info> | 2024-03-15 03:06:35 +0000 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2024-03-15 03:06:35 +0000 |
commit | 12679ab7d3c2a210f4123163671b532b8b55d5f9 (patch) | |
tree | 0060d13467186ad977f4e73488ee20dd6c0017ab /systems/texlive/tlnet/tlpkg/tlperl/lib/Pod/Man.pm | |
parent | 62170822e034fdd3f81de7274835d0d3b0467100 (diff) |
CTAN sync 202403150306
Diffstat (limited to 'systems/texlive/tlnet/tlpkg/tlperl/lib/Pod/Man.pm')
-rw-r--r-- | systems/texlive/tlnet/tlpkg/tlperl/lib/Pod/Man.pm | 1576 |
1 files changed, 965 insertions, 611 deletions
diff --git a/systems/texlive/tlnet/tlpkg/tlperl/lib/Pod/Man.pm b/systems/texlive/tlnet/tlpkg/tlperl/lib/Pod/Man.pm index d7c029357a..5ed95bbc90 100644 --- a/systems/texlive/tlnet/tlpkg/tlperl/lib/Pod/Man.pm +++ b/systems/texlive/tlnet/tlpkg/tlperl/lib/Pod/Man.pm @@ -14,36 +14,33 @@ package Pod::Man; -use 5.008; +use 5.010; use strict; use warnings; -use subs qw(makespace); -use vars qw(@ISA %ESCAPES $PREAMBLE $VERSION); - use Carp qw(carp croak); use Pod::Simple (); # Conditionally import Encode and set $HAS_ENCODE if it is available. This is # required to support building as part of Perl core, since podlators is built # before Encode is. -our $HAS_ENCODE; +my $HAS_ENCODE; BEGIN { $HAS_ENCODE = eval { require Encode }; } -@ISA = qw(Pod::Simple); - -$VERSION = '4.14'; - -# Set the debugging level. If someone has inserted a debug function into this -# class already, use that. Otherwise, use any Pod::Simple debug function -# that's defined, and failing that, define a debug level of 10. -BEGIN { - my $parent = defined (&Pod::Simple::DEBUG) ? \&Pod::Simple::DEBUG : undef; - unless (defined &DEBUG) { - *DEBUG = $parent || sub () { 10 }; - } +our @ISA = qw(Pod::Simple); +our $VERSION = '5.01'; + +# Ensure that $Pod::Simple::nbsp and $Pod::Simple::shy are available. Code +# taken from Pod::Simple 3.32, but was only added in 3.30. +my ($NBSP, $SHY); +if ($Pod::Simple::VERSION ge 3.30) { + $NBSP = $Pod::Simple::nbsp; + $SHY = $Pod::Simple::shy; +} else { + $NBSP = chr utf8::unicode_to_native(0xA0); + $SHY = chr utf8::unicode_to_native(0xAD); } # Import the ASCII constant from Pod::Simple. This is true iff we're in an @@ -51,15 +48,12 @@ BEGIN { # generally only false for EBCDIC. BEGIN { *ASCII = \&Pod::Simple::ASCII } -# Pretty-print a data structure. Only used for debugging. -BEGIN { *pretty = \&Pod::Simple::pretty } - # Formatting instructions for various types of blocks. cleanup makes hyphens # hard, adds spaces between consecutive underscores, and escapes backslashes. # convert translates characters into escapes. guesswork means to apply the -# transformations done by the guesswork sub. literal says to protect literal -# quotes from being turned into UTF-8 quotes. By default, all transformations -# are on except literal, but some elements override. +# transformations done by the guesswork sub (if enabled). literal says to +# protect literal quotes from being turned into UTF-8 quotes. By default, all +# transformations are on except literal, but some elements override. # # DEFAULT specifies the default settings. All other elements should list only # those settings that they are overriding. Data indicates =for roff blocks, @@ -75,6 +69,98 @@ my %FORMATTING = ( X => { cleanup => 0, guesswork => 0 }, ); +# Try to map an encoding as understood by Perl Encode to an encoding +# understood by groff's preconv. Encode doesn't care about hyphens or +# capitalization, but preconv does. The key is the canonicalized Encode +# encoding, and the value is something preconv might understand. +# +# FreeBSD mandoc only understands utf-8 and iso-latin-1 as of 2022-09-24. +# groff preconv prefers iso-8859-1, but also understands iso-latin-1, so +# convert ISO-8859-1 to iso-latin-1 for FreeBSD. +my %ENCODINGS = ( + ascii => 'us-ascii', + big5 => 'big5', + big5eten => 'big5', + cp950 => 'big5', + cp1047 => 'cp1047', + euccn => 'gb2312', + eucjp => 'euc-jp', + euckr => 'euc-kr', + gb2312 => 'gb2312', + gb2312raw => 'gb2312', + iso88591 => 'iso-latin-1', + iso88592 => 'iso-8859-2', + iso88595 => 'iso-8859-5', + iso88597 => 'iso-8859-7', + iso88599 => 'iso-8859-9', + iso885913 => 'iso-8859-13', + iso885915 => 'iso-8859-15', + koi8r => 'koi8-r', + latin1 => 'iso-8859-1', + usascii => 'us-ascii', + utf8 => 'utf-8', + utf16 => 'utf-16', + utf16be => 'utf-16be', + utf16le => 'utf-16le', +); + +############################################################################## +# Translation tables +############################################################################## + +# The following table is adapted from Tom Christiansen's pod2man. It is only +# used with roff output. It assumes that the standard preamble has already +# been printed, since that's what defines all of the accent marks. We really +# want to do something better than this when *roff actually supports other +# character sets itself, since these results are pretty poor. +# +# This only works in an ASCII world. What to do in a non-ASCII world is very +# unclear, so we just output what we get and hope for the best. +my %ESCAPES; +@ESCAPES{0xA0 .. 0xFF} = ( + $NBSP, undef, undef, undef, undef, undef, undef, undef, + undef, undef, undef, undef, undef, $SHY, undef, undef, + + undef, undef, undef, undef, undef, undef, undef, undef, + undef, undef, undef, undef, undef, undef, undef, undef, + + "A\\*`", "A\\*'", "A\\*^", "A\\*~", "A\\*:", "A\\*o", "\\*(Ae", "C\\*,", + "E\\*`", "E\\*'", "E\\*^", "E\\*:", "I\\*`", "I\\*'", "I\\*^", "I\\*:", + + "\\*(D-", "N\\*~", "O\\*`", "O\\*'", "O\\*^", "O\\*~", "O\\*:", undef, + "O\\*/", "U\\*`", "U\\*'", "U\\*^", "U\\*:", "Y\\*'", "\\*(Th", "\\*8", + + "a\\*`", "a\\*'", "a\\*^", "a\\*~", "a\\*:", "a\\*o", "\\*(ae", "c\\*,", + "e\\*`", "e\\*'", "e\\*^", "e\\*:", "i\\*`", "i\\*'", "i\\*^", "i\\*:", + + "\\*(d-", "n\\*~", "o\\*`", "o\\*'", "o\\*^", "o\\*~", "o\\*:", undef, + "o\\*/" , "u\\*`", "u\\*'", "u\\*^", "u\\*:", "y\\*'", "\\*(th", "y\\*:", +) if ASCII; + +############################################################################## +# Utility functions +############################################################################## + +# Quote an argument to a macro. +# +# $arg - Intended argument to the macro +# +# Returns: $arg suitably escaped and quoted +sub _quote_macro_argument { + my ($arg) = @_; + if (length($arg) > 0 && $arg !~ m{ [\s\"] }xms) { + return $arg; + } + $arg =~ s{ \" }{""}xmsg; + return qq("$arg"); +} + +# Returns whether the given encoding needs a call to Encode::encode. +sub _needs_encode { + my ($encoding) = @_; + return $encoding ne 'roff' && $encoding ne 'groff'; +} + ############################################################################## # Object initialization ############################################################################## @@ -88,9 +174,6 @@ sub new { my $class = shift; my $self = $class->SUPER::new; - # Tell Pod::Simple not to handle S<> by automatically inserting . - $self->nbsp_for_S (1); - # Tell Pod::Simple to keep whitespace whenever possible. if (my $preserve_whitespace = $self->can ('preserve_whitespace')) { $self->$preserve_whitespace (1); @@ -109,63 +192,84 @@ sub new { # to put them in our object as hash keys and values. This could cause # problems if we ever clash with Pod::Simple's own internal class # variables. - %$self = (%$self, @_); + my %opts = @_; + my @opts = map { ("opt_$_", $opts{$_}) } keys %opts; + %$self = (%$self, @opts); + + # Pod::Simple uses encoding internally, so we need to store it as + # ENCODING. Set the default to UTF-8 if not specified. + # + # Degrade to the old roff encoding if Encode is not available. + # + # Suppress the warning message when PERL_CORE is set, indicating this is + # running as part of the core Perl build. Perl builds podlators (and all + # pure Perl modules) before Encode and other XS modules, so Encode won't + # yet be available. Rely on the Perl core build to generate man pages + # later, after all the modules are available, so that UTF-8 handling will + # be correct. + my %options = @_; + if (defined $self->{opt_encoding}) { + $$self{ENCODING} = $self->{opt_encoding}; + } elsif (ASCII) { + $$self{ENCODING} = 'UTF-8'; + } else { + $$self{ENCODING} = 'groff'; + } + if (_needs_encode($$self{ENCODING}) && !$HAS_ENCODE) { + if (!$ENV{PERL_CORE}) { + carp ('encoding requested but Encode module not available,' + . ' falling back to groff escapes'); + } + $$self{ENCODING} = 'groff'; + } # Send errors to stderr if requested. - if ($$self{stderr} and not $$self{errors}) { - $$self{errors} = 'stderr'; + if ($self->{opt_stderr} and not $self->{opt_errors}) { + $self->{opt_errors} = 'stderr'; } - delete $$self{stderr}; + delete $self->{opt_stderr}; # Validate the errors parameter and act on it. - if (not defined $$self{errors}) { - $$self{errors} = 'pod'; - } - if ($$self{errors} eq 'stderr' || $$self{errors} eq 'die') { + $self->{opt_errors} //= 'pod'; + if ($self->{opt_errors} eq 'stderr' || $self->{opt_errors} eq 'die') { $self->no_errata_section (1); $self->complain_stderr (1); - if ($$self{errors} eq 'die') { - $$self{complain_die} = 1; + if ($self->{opt_errors} eq 'die') { + $self->{complain_die} = 1; } - } elsif ($$self{errors} eq 'pod') { + } elsif ($self->{opt_errors} eq 'pod') { $self->no_errata_section (0); $self->complain_stderr (0); - } elsif ($$self{errors} eq 'none') { + } elsif ($self->{opt_errors} eq 'none') { $self->no_errata_section (1); $self->no_whining (1); } else { - croak (qq(Invalid errors setting: "$$self{errors}")); - } - delete $$self{errors}; - - # Degrade back to non-utf8 if Encode is not available. - # - # Suppress the warning message when PERL_CORE is set, indicating this is - # running as part of the core Perl build. Perl builds podlators (and all - # pure Perl modules) before Encode and other XS modules, so Encode won't - # yet be available. Rely on the Perl core build to generate man pages - # later, after all the modules are available, so that UTF-8 handling will - # be correct. - if ($$self{utf8} and !$HAS_ENCODE) { - if (!$ENV{PERL_CORE}) { - carp ('utf8 mode requested but Encode module not available,' - . ' falling back to non-utf8'); - } - delete $$self{utf8}; + croak (qq(Invalid errors setting: "$self->{opt_errors}")); } + delete $self->{opt_errors}; # Initialize various other internal constants based on our arguments. $self->init_fonts; $self->init_quotes; $self->init_page; - # For right now, default to turning on all of the magic. - $$self{MAGIC_CPP} = 1; - $$self{MAGIC_EMDASH} = 1; - $$self{MAGIC_FUNC} = 1; - $$self{MAGIC_MANREF} = 1; - $$self{MAGIC_SMALLCAPS} = 1; - $$self{MAGIC_VARS} = 1; + # Configure guesswork based on options. + my $guesswork = $self->{opt_guesswork} || q{}; + my %guesswork = map { $_ => 1 } split(m{,}xms, $guesswork); + if (!%guesswork || $guesswork{all}) { + #<<< + $$self{GUESSWORK} = { + functions => 1, + manref => 1, + quoting => 1, + variables => 1, + }; + #>>> + } elsif ($guesswork{none}) { + $$self{GUESSWORK} = {}; + } else { + $$self{GUESSWORK} = {%guesswork}; + } return $self; } @@ -182,29 +286,41 @@ sub init_fonts { # Figure out the fixed-width font. If user-supplied, make sure that they # are the right length. - for (qw/fixed fixedbold fixeditalic fixedbolditalic/) { - my $font = $$self{$_}; - if (defined ($font) && (length ($font) < 1 || length ($font) > 2)) { - croak qq(roff font should be 1 or 2 chars, not "$font"); + for (qw(fixed fixedbold fixeditalic fixedbolditalic)) { + my $font = $self->{"opt_$_"}; + if (defined($font) && (length($font) < 1 || length($font) > 2)) { + croak(qq(roff font should be 1 or 2 chars, not "$font")); } } # Set the default fonts. We can't be sure portably across different # implementations what fixed bold-italic may be called (if it's even # available), so default to just bold. - $$self{fixed} ||= 'CW'; - $$self{fixedbold} ||= 'CB'; - $$self{fixeditalic} ||= 'CI'; - $$self{fixedbolditalic} ||= 'CB'; + #<<< + $self->{opt_fixed} ||= 'CW'; + $self->{opt_fixedbold} ||= 'CB'; + $self->{opt_fixeditalic} ||= 'CI'; + $self->{opt_fixedbolditalic} ||= 'CB'; + #>>> # Set up a table of font escapes. First number is fixed-width, second is # bold, third is italic. - $$self{FONTS} = { '000' => '\fR', '001' => '\fI', - '010' => '\fB', '011' => '\f(BI', - '100' => toescape ($$self{fixed}), - '101' => toescape ($$self{fixeditalic}), - '110' => toescape ($$self{fixedbold}), - '111' => toescape ($$self{fixedbolditalic}) }; + $self->{FONTS} = { + '000' => '\fR', + '001' => '\fI', + '010' => '\fB', + '011' => '\f(BI', + '100' => toescape($self->{opt_fixed}), + '101' => toescape($self->{opt_fixeditalic}), + '110' => toescape($self->{opt_fixedbold}), + '111' => toescape($self->{opt_fixedbolditalic}), + }; + + # Precalculate a regex that matches all fixed-width fonts, which will be + # used later by switchquotes. + my @fixedpat = map { quotemeta($self->{FONTS}{$_}) } qw(100 101 110 111); + my $fixedpat = join('|', @fixedpat); + $self->{FIXEDPAT} = qr{ $fixedpat }xms; } # Initialize the quotes that we'll be using for C<> text. This requires some @@ -215,32 +331,28 @@ sub init_quotes { my ($self) = (@_); # Handle the quotes option first, which sets both quotes at once. - $$self{quotes} ||= '"'; - if ($$self{quotes} eq 'none') { + $self->{opt_quotes} ||= '"'; + if ($self->{opt_quotes} eq 'none') { $$self{LQUOTE} = $$self{RQUOTE} = ''; - } elsif (length ($$self{quotes}) == 1) { - $$self{LQUOTE} = $$self{RQUOTE} = $$self{quotes}; - } elsif (length ($$self{quotes}) % 2 == 0) { - my $length = length ($$self{quotes}) / 2; - $$self{LQUOTE} = substr ($$self{quotes}, 0, $length); - $$self{RQUOTE} = substr ($$self{quotes}, $length); + } elsif (length ($self->{opt_quotes}) == 1) { + $$self{LQUOTE} = $$self{RQUOTE} = $self->{opt_quotes}; + } elsif (length ($self->{opt_quotes}) % 2 == 0) { + my $length = length ($self->{opt_quotes}) / 2; + $$self{LQUOTE} = substr ($self->{opt_quotes}, 0, $length); + $$self{RQUOTE} = substr ($self->{opt_quotes}, $length); } else { - croak(qq(Invalid quote specification "$$self{quotes}")) + croak(qq(Invalid quote specification "$self->{opt_quotes}")) } # Now handle the lquote and rquote options. - if (defined $$self{lquote}) { - $$self{LQUOTE} = $$self{lquote} eq 'none' ? q{} : $$self{lquote}; + if (defined($self->{opt_lquote})) { + $self->{opt_lquote} = q{} if $self->{opt_lquote} eq 'none'; + $$self{LQUOTE} = $self->{opt_lquote}; } - if (defined $$self{rquote}) { - $$self{RQUOTE} = $$self{rquote} eq 'none' ? q{} : $$self{rquote}; + if (defined $self->{opt_rquote}) { + $self->{opt_rquote} = q{} if $self->{opt_rquote} eq 'none'; + $$self{RQUOTE} = $self->{opt_rquote}; } - - # Double the first quote; note that this should not be s///g as two double - # quotes is represented in *roff as three double quotes, not four. Weird, - # I know. - $$self{LQUOTE} =~ s/\"/\"\"/; - $$self{RQUOTE} =~ s/\"/\"\"/; } # Initialize the page title information and indentation from our arguments. @@ -254,17 +366,9 @@ sub init_page { # Set the defaults for page titles and indentation if the user didn't # override anything. - $$self{center} = 'User Contributed Perl Documentation' - unless defined $$self{center}; - $$self{release} = 'perl v' . $version - unless defined $$self{release}; - $$self{indent} = 4 - unless defined $$self{indent}; - - # Double quotes in things that will be quoted. - for (qw/center release/) { - $$self{$_} =~ s/\"/\"\"/g if $$self{$_}; - } + $self->{opt_center} //= 'User Contributed Perl Documentation'; + $self->{opt_release} //= 'perl v' . $version; + $self->{opt_indent} //= 4; } ############################################################################## @@ -289,7 +393,6 @@ sub init_page { # according to the current formatting instructions as we do. sub _handle_text { my ($self, $text) = @_; - DEBUG > 3 and print "== $text\n"; my $tag = $$self{PENDING}[-1]; $$tag[2] .= $self->format_text ($$tag[1], $text); } @@ -308,7 +411,6 @@ sub method_for_element { # text and nested elements. Otherwise, if start_element is defined, call it. sub _handle_element_start { my ($self, $element, $attrs) = @_; - DEBUG > 3 and print "++ $element (<", join ('> <', %$attrs), ">)\n"; my $method = $self->method_for_element ($element); # If we have a command handler, we need to accumulate the contents of the @@ -316,7 +418,6 @@ sub _handle_element_start { # <Para> and the formatting codes so that IN_NAME isn't still set for the # first heading after the NAME heading. if ($self->can ("cmd_$method")) { - DEBUG > 2 and print "<$element> starts saving a tag\n"; $$self{IN_NAME} = 0 if ($element ne 'Para' && length ($element) > 1); # How we're going to format embedded text blocks depends on the tag @@ -328,11 +429,8 @@ sub _handle_element_start { %{ $FORMATTING{$element} || {} }, }; push (@{ $$self{PENDING} }, [ $attrs, $formatting, '' ]); - DEBUG > 4 and print "Pending: [", pretty ($$self{PENDING}), "]\n"; } elsif (my $start_method = $self->can ("start_$method")) { $self->$start_method ($attrs, ''); - } else { - DEBUG > 2 and print "No $method start method, skipping\n"; } } @@ -341,16 +439,12 @@ sub _handle_element_start { # an end_ method for the element, call that. sub _handle_element_end { my ($self, $element) = @_; - DEBUG > 3 and print "-- $element\n"; my $method = $self->method_for_element ($element); # If we have a command handler, pull off the pending text and pass it to # the handler along with the saved attribute hash. if (my $cmd_method = $self->can ("cmd_$method")) { - DEBUG > 2 and print "</$element> stops saving a tag\n"; my $tag = pop @{ $$self{PENDING} }; - DEBUG > 4 and print "Popped: [", pretty ($tag), "]\n"; - DEBUG > 4 and print "Pending: [", pretty ($$self{PENDING}), "]\n"; my $text = $self->$cmd_method ($$tag[0], $$tag[2]); if (defined $text) { if (@{ $$self{PENDING} } > 1) { @@ -361,8 +455,6 @@ sub _handle_element_end { } } elsif (my $end_method = $self->can ("end_$method")) { $self->$end_method (); - } else { - DEBUG > 2 and print "No $method end method, skipping\n"; } } @@ -381,31 +473,40 @@ sub format_text { my $literal = $$options{literal}; # Cleanup just tidies up a few things, telling *roff that the hyphens are - # hard, putting a bit of space between consecutive underscores, and - # escaping backslashes. Be careful not to mangle our character - # translations by doing this before processing character translation. + # hard, putting a bit of space between consecutive underscores, escaping + # backslashes, and converting zero-width spaces to zero-width break + # points. if ($cleanup) { $text =~ s/\\/\\e/g; $text =~ s/-/\\-/g; $text =~ s/_(?=_)/_\\|/g; + $text =~ s/\x{200B}/\\:/g; } - # Normally we do character translation, but we won't even do that in - # <Data> blocks or if UTF-8 output is desired. - if ($convert && !$$self{utf8} && ASCII) { - $text =~ s/([^\x00-\x7F])/$ESCAPES{ord ($1)} || "X"/eg; + # Except in <Data> blocks, if groff or roff encoding is requested and + # we're in an ASCII environment, do the encoding. For EBCDIC, we just + # write what we get and hope for the best. Leave non-breaking spaces and + # soft hyphens alone; we'll convert those at the last minute. + if ($convert) { + if (ASCII) { + if ($$self{ENCODING} eq 'groff') { + $text =~ s{ ([^\x00-\x7F\xA0\xAD]) }{ + '\\[u' . sprintf('%04X', ord($1)) . ']' + }xmsge; + } elsif ($$self{ENCODING} eq 'roff') { + $text =~ s/([^\x00-\x7F\xA0\xAD])/$ESCAPES{ord ($1)} || "X"/eg; + } + } } # Ensure that *roff doesn't convert literal quotes to UTF-8 single quotes, - # but don't mess up our accept escapes. + # but don't mess up accent escapes. if ($literal) { $text =~ s/(?<!\\\*)\'/\\*\(Aq/g; $text =~ s/(?<!\\\*)\`/\\\`/g; } - # If guesswork is asked for, do that. This involves more substantial - # formatting based on various heuristics that may only be appropriate for - # particular documents. + # If guesswork is is viable for this block, do that. if ($guesswork) { $text = $self->guesswork ($text); } @@ -420,31 +521,45 @@ sub quote_literal { my $self = shift; local $_ = shift; + # If in NAME section, just return an ASCII quoted string to avoid + # confusing tools like whatis. + if ($$self{IN_NAME}) { + return $self->{LQUOTE} . $_ . $self->{RQUOTE}; + } + # A regex that matches the portion of a variable reference that's the # array or hash index, separated out just because we want to use it in # several places in the following regex. - my $index = '(?: \[.*\] | \{.*\} )?'; - - # If in NAME section, just return an ASCII quoted string to avoid - # confusing tools like whatis. - return qq{"$_"} if $$self{IN_NAME}; + my $index = '(?: \[[^]]+\] | \{[^}]+\} )?'; # Check for things that we don't want to quote, and if we find any of # them, return the string with just a font change and no quoting. + # + # Traditionally, Pod::Man has not quoted Perl variables, functions, + # numbers, or hex constants, but this is not always desirable. Make this + # optional on the quoting guesswork flag. + my $extra = qr{(?!)}xms; # never matches + if ($$self{GUESSWORK}{quoting}) { + $extra = qr{ + \$+ [\#^]? \S $index # special ($^F, $") + | [\$\@%&*]+ \#? [:\'\w]+ $index # plain var or func + | [\$\@%&*]* [:\'\w]+ + (?: \\-> )? \(\s*[^\s,\)]*\s*\) # 0/1-arg func call + | (?: [+] || \\- )? ( \d[\d.]* | \.\d+ ) + (?: [eE] (?: [+] || \\- )? \d+ )? # a number + | 0x [a-fA-F\d]+ # a hex constant + }xms; + } m{ ^\s* (?: - ( [\'\`\"] ) .* \1 # already quoted - | \\\*\(Aq .* \\\*\(Aq # quoted and escaped - | \\?\` .* ( \' | \\\*\(Aq ) # `quoted' - | \$+ [\#^]? \S $index # special ($^Foo, $") - | [\$\@%&*]+ \#? [:\'\w]+ $index # plain var or func - | [\$\@%&*]* [:\'\w]+ (?: -> )? \(\s*[^\s,]\s*\) # 0/1-arg func call - | [-+]? ( \d[\d.]* | \.\d+ ) (?: [eE][-+]?\d+ )? # a number - | 0x [a-fA-F\d]+ # a hex constant + ( [\'\"] ) .* \1 # already quoted + | \\\*\(Aq .* \\\*\(Aq # quoted and escaped + | \\?\` .* ( \' | \\?\` | \\\*\(Aq ) # `quoted' or `quoted` + | $extra ) \s*\z - }xso and return '\f(FS' . $_ . '\f(FE'; + }xms and return '\f(FS' . $_ . '\f(FE'; # If we didn't return, go ahead and quote the text. return '\f(FS\*(C`' . $_ . "\\*(C'\\f(FE"; @@ -453,10 +568,8 @@ sub quote_literal { # Takes a text block to perform guesswork on. Returns the text block with # formatting codes added. This is the code that marks up various Perl # constructs and things commonly used in man pages without requiring the user -# to add any explicit markup, and is applied to all non-literal text. We're -# guaranteed that the text we're applying guesswork to does not contain any -# *roff formatting codes. Note that the inserted font sequences must be -# treated later with mapfonts or textmapfonts. +# to add any explicit markup, and is applied to all non-literal text. Note +# that the inserted font sequences must be treated later with mapfonts. # # This method is very fragile, both in the regular expressions it uses and in # the ordering of those modifications. Care and testing is required when @@ -464,7 +577,6 @@ sub quote_literal { sub guesswork { my $self = shift; local $_ = shift; - DEBUG > 5 and print " Guesswork called on [$_]\n"; # By the time we reach this point, all hyphens will be escaped by adding a # backslash. We want to undo that escaping if they're part of regular @@ -475,9 +587,9 @@ sub guesswork { # Note that this is not user-controllable; we pretty much have to do this # transformation or *roff will mangle the output in unacceptable ways. s{ - ( (?:\G|^|\s) [\(\"]* [a-zA-Z] ) ( \\- )? + ( (?:\G|^|\s|$NBSP) [\(\"]* [a-zA-Z] ) ( \\- )? ( (?: [a-zA-Z\']+ \\-)+ ) - ( [a-zA-Z\']+ ) (?= [\)\".?!,;:]* (?:\s|\Z|\\\ ) ) + ( [a-zA-Z\']+ ) (?= [\)\".?!,;:]* (?:\s|$NBSP|\Z|\\\ ) ) \b } { my ($prefix, $hyphen, $main, $suffix) = ($1, $2, $3, $4); @@ -486,52 +598,17 @@ sub guesswork { $prefix . $hyphen . $main . $suffix; }egx; - # Translate "--" into a real em-dash if it's used like one. This means - # that it's either surrounded by whitespace, it follows a regular word, or - # it occurs between two regular words. - if ($$self{MAGIC_EMDASH}) { - s{ (\s) \\-\\- (\s) } { $1 . '\*(--' . $2 }egx; - s{ (\b[a-zA-Z]+) \\-\\- (\s|\Z|[a-zA-Z]+\b) } { $1 . '\*(--' . $2 }egx; - } - - # Make words in all-caps a little bit smaller; they look better that way. - # However, we don't want to change Perl code (like @ARGV), nor do we want - # to fix the MIME in MIME-Version since it looks weird with the - # full-height V. - # - # We change only a string of all caps (2) either at the beginning of the - # line or following regular punctuation (like quotes) or whitespace (1), - # and followed by either similar punctuation, an em-dash, or the end of - # the line (3). - # - # Allow the text we're changing to small caps to include double quotes, - # commas, newlines, and periods as long as it doesn't otherwise interrupt - # the string of small caps and still fits the criteria. This lets us turn - # entire warranty disclaimers in man page output into small caps. - if ($$self{MAGIC_SMALLCAPS}) { - s{ - ( ^ | [\s\(\"\'\`\[\{<>] | \\[ ] ) # (1) - ( [A-Z] [A-Z] (?: \s? [/A-Z+:\d_\$&] | \\- | \s? [.,\"] )* ) # (2) - (?= [\s>\}\]\(\)\'\".?!,;] | \\*\(-- | \\[ ] | $ ) # (3) - } { - $1 . '\s-1' . $2 . '\s0' - }egx; - } - - # Note that from this point forward, we have to adjust for \s-1 and \s-0 - # strings inserted around things that we've made small-caps if later - # transforms should work on those strings. - # Embolden functions in the form func(), including functions that are in - # all capitals, but don't embolden if there's anything between the parens. + # all capitals, but don't embolden if there's anything inside the parens. # The function must start with an alphabetic character or underscore and # then consist of word characters or colons. - if ($$self{MAGIC_FUNC}) { + if ($$self{GUESSWORK}{functions}) { s{ - ( \b | \\s-1 ) - ( [A-Za-z_] ([:\w] | \\s-?[01])+ \(\) ) + (?<! \\ ) + \b + ( [A-Za-z_] [:\w]+ \(\) ) } { - $1 . '\f(BS' . $2 . '\f(BE' + '\f(BS' . $1 . '\f(BE' }egx; } @@ -542,21 +619,21 @@ sub guesswork { # configuration file man pages), or colons, and n is a single digit, # optionally followed by some number of lowercase letters. Note that this # does not recognize man page references like perl(l) or socket(3SOCKET). - if ($$self{MAGIC_MANREF}) { + if ($$self{GUESSWORK}{manref}) { s{ - ( \b | \\s-1 ) - (?<! \\ ) # rule out \s0(1) - ( [A-Za-z_] (?:[.:\w] | \\- | \\s-?[01])+ ) + \b + (?<! \\ ) # rule out \e0(1) + ( [A-Za-z_] (?:[.:\w] | \\-)+ ) ( \( \d [a-z]* \) ) } { - $1 . '\f(BS' . $2 . '\f(BE\|' . $3 + '\f(BS' . $1 . '\f(BE\|' . $2 }egx; } # Convert simple Perl variable references to a fixed-width font. Be # careful not to convert functions, though; there are too many subtleties # with them to want to perform this transformation. - if ($$self{MAGIC_VARS}) { + if ($$self{GUESSWORK}{variables}) { s{ ( ^ | \s+ ) ( [\$\@%] [\w:]+ ) @@ -566,19 +643,7 @@ sub guesswork { }egx; } - # Fix up double quotes. Unfortunately, we miss this transformation if the - # quoted text contains any code with formatting codes and there's not much - # we can effectively do about that, which makes it somewhat unclear if - # this is really a good idea. - s{ \" ([^\"]+) \" } { '\*(L"' . $1 . '\*(R"' }egx; - - # Make C++ into \*(C+, which is a squinched version. - if ($$self{MAGIC_CPP}) { - s{ \b C\+\+ } {\\*\(C+}gx; - } - # Done. - DEBUG > 5 and print " Guesswork returning [$_]\n"; return $_; } @@ -592,111 +657,106 @@ sub guesswork { # the right start and end codes. # # We add this level of complexity because the old pod2man didn't get code like -# B<someI<thing> else> right; after I<> it switched back to normal text rather -# than bold. We take care of this by using variables that state whether bold, -# italic, or fixed are turned on as a combined pointer to our current font -# sequence, and set each to the number of current nestings of start tags for -# that font. +# B<< someI<thing> else>> right. After I<> it switched back to normal text +# rather than bold. We take care of this by using variables that state +# whether bold, italic, or fixed are turned on as a combined pointer to our +# current font sequence, and set each to the number of current nestings of +# start tags for that font. +# +# The base font must be either \fP or \fR. \fP changes to the previous font, +# but only one previous font is kept. Unfortunately, there is a bug in +# Solaris 2.6 nroff (not present in GNU groff) where the sequence +# \fB\fP\f(CW\fP leaves the font set to B rather than R, presumably because +# \f(CW doesn't actually do a font change. Because of this, we prefer to use +# \fR where possible. +# +# Unfortunately, this isn't possible for arguments to heading macros, since +# there we don't know what the outside level font is. In that case, arrange +# things so that the outside font is always the "previous" font and end with +# \fP instead of \fR. Idea from Zack Weinberg. # -# \fP changes to the previous font, but only one previous font is kept. We -# don't know what the outside level font is; normally it's R, but if we're -# inside a heading it could be something else. So arrange things so that the -# outside font is always the "previous" font and end with \fP instead of \fR. -# Idea from Zack Weinberg. +# This function used to be much simpler outside of macro arguments because it +# went directly from \fB to \f(CW and relied on \f(CW clearing bold since it +# wasn't \f(CB. Unfortunately, while this works for mandoc, this is not how +# groff works; \fBfoo\f(CWbar still prints bar in bold. Therefore, we force +# the font back to the base font before each font change. sub mapfonts { - my ($self, $text) = @_; + my ($self, $text, $base) = @_; + + # The closure used to process each font escape, expected to be called from + # the right-hand side of an s/// expression. my ($fixed, $bold, $italic) = (0, 0, 0); my %magic = (F => \$fixed, B => \$bold, I => \$italic); my $last = '\fR'; - $text =~ s< - \\f\((.)(.) - > < - my $sequence = ''; - my $f; - if ($last ne '\fR') { $sequence = '\fP' } - ${ $magic{$1} } += ($2 eq 'S') ? 1 : -1; - $f = $$self{FONTS}{ ($fixed && 1) . ($bold && 1) . ($italic && 1) }; - if ($f eq $last) { - ''; - } else { - if ($f ne '\fR') { $sequence .= $f } - $last = $f; - $sequence; + my $process = sub { + my ($style, $start_stop) = @_; + my $sequence = ($last ne '\fR') ? $base : q{}; + ${ $magic{$style} } += ($start_stop eq 'S') ? 1 : -1; + my $f = $self->{FONTS}{($fixed && 1) . ($bold && 1) . ($italic && 1)}; + return q{} if ($f eq $last); + if ($f ne '\fR') { + $sequence .= $f; } - >gxe; - return $text; -} + $last = $f; + return $sequence; + }; + + # Now, do the actual work. + $text =~ s{ \\f\((.)(.) }{$process->($1, $2)}xmsge; + + # We can do a bit of cleanup by collapsing sequences like \fR\fB\fR\fI + # into just \fI. + $text =~ s{ (?: \\fR )? (?: \\f (.|\(..) \\fR )+ }{\\fR}xms; -# Unfortunately, there is a bug in Solaris 2.6 nroff (not present in GNU -# groff) where the sequence \fB\fP\f(CW\fP leaves the font set to B rather -# than R, presumably because \f(CW doesn't actually do a font change. To work -# around this, use a separate textmapfonts for text blocks where the default -# font is always R and only use the smart mapfonts for headings. -sub textmapfonts { - my ($self, $text) = @_; - my ($fixed, $bold, $italic) = (0, 0, 0); - my %magic = (F => \$fixed, B => \$bold, I => \$italic); - $text =~ s< - \\f\((.)(.) - > < - ${ $magic{$1} } += ($2 eq 'S') ? 1 : -1; - $$self{FONTS}{ ($fixed && 1) . ($bold && 1) . ($italic && 1) }; - >gxe; return $text; } # Given a command and a single argument that may or may not contain double -# quotes, handle double-quote formatting for it. If there are no double -# quotes, just return the command followed by the argument in double quotes. -# If there are double quotes, use an if statement to test for nroff, and for -# nroff output the command followed by the argument in double quotes with -# embedded double quotes doubled. For other formatters, remap paired double -# quotes to LQUOTE and RQUOTE. +# quotes and fixed-width text, handle double-quote formatting for it. If +# there is no fixed-width text, just return the command followed by the +# argument with proper quoting. If there is fixed-width text, work around a +# Solaris nroff bug with fixed-width fonts by converting fixed-width to +# regular fonts (nroff sees no difference). sub switchquotes { my ($self, $command, $text, $extra) = @_; - $text =~ s/\\\*\([LR]\"/\"/g; - - # We also have to deal with \*C` and \*C', which are used to add the - # quotes around C<> text, since they may expand to " and if they do this - # confuses the .SH macros and the like no end. Expand them ourselves. - # Also separate troff from nroff if there are any fixed-width fonts in use - # to work around problems with Solaris nroff. - my $c_is_quote = ($$self{LQUOTE} =~ /\"/) || ($$self{RQUOTE} =~ /\"/); - my $fixedpat = join '|', @{ $$self{FONTS} }{'100', '101', '110', '111'}; - $fixedpat =~ s/\\/\\\\/g; - $fixedpat =~ s/\(/\\\(/g; - if ($text =~ m/\"/ || $text =~ m/$fixedpat/) { - $text =~ s/\"/\"\"/g; + + # Separate troff from nroff if there are any fixed-width fonts in use to + # work around problems with Solaris nroff. + if ($text =~ $self->{FIXEDPAT}) { my $nroff = $text; my $troff = $text; - $troff =~ s/\"\"([^\"]*)\"\"/\`\`$1\'\'/g; - if ($c_is_quote and $text =~ m/\\\*\(C[\'\`]/) { - $nroff =~ s/\\\*\(C\`/$$self{LQUOTE}/g; - $nroff =~ s/\\\*\(C\'/$$self{RQUOTE}/g; - $troff =~ s/\\\*\(C[\'\`]//g; - } - $nroff = qq("$nroff") . ($extra ? " $extra" : ''); - $troff = qq("$troff") . ($extra ? " $extra" : ''); # Work around the Solaris nroff bug where \f(CW\fP leaves the font set # to Roman rather than the actual previous font when used in headings. # troff output may still be broken, but at least we can fix nroff by # just switching the font changes to the non-fixed versions. - my $font_end = "(?:\\f[PR]|\Q$$self{FONTS}{100}\E)"; - $nroff =~ s/\Q$$self{FONTS}{100}\E(.*?)\\f([PR])/$1/g; - $nroff =~ s/\Q$$self{FONTS}{101}\E(.*?)$font_end/\\fI$1\\fP/g; - $nroff =~ s/\Q$$self{FONTS}{110}\E(.*?)$font_end/\\fB$1\\fP/g; - $nroff =~ s/\Q$$self{FONTS}{111}\E(.*?)$font_end/\\f\(BI$1\\fP/g; + my $font_end = qr{ (?: \\f[PR] | \Q$self->{FONTS}{100}\E ) }xms; + $nroff =~ s{\Q$self->{FONTS}{100}\E(.*?)\\f([PR])}{$1}xmsg; + $nroff =~ s{\Q$self->{FONTS}{101}\E}{\\fI}xmsg; + $nroff =~ s{\Q$self->{FONTS}{110}\E}{\\fB}xmsg; + $nroff =~ s{\Q$self->{FONTS}{111}\E}{\\f\(BI}xmsg; + + # We have to deal with \*C` and \*C', which are used to add the quotes + # around C<> text, since they may expand to " and if they do this + # confuses the .SH macros and the like no end. Expand them ourselves. + my $c_is_quote = index("$self->{LQUOTE}$self->{RQUOTE}", qq(\")) != -1; + if ($c_is_quote && $text =~ m{ \\[*]\(C[\'\`] }xms) { + $nroff =~ s{ \\[*]\(C\` }{$self->{LQUOTE}}xmsg; + $nroff =~ s{ \\[*]\(C\' }{$self->{RQUOTE}}xmsg; + $troff =~ s{ \\[*]\(C[\'\`] }{}xmsg; + } # Now finally output the command. Bother with .ie only if the nroff # and troff output aren't the same. + $nroff = _quote_macro_argument($nroff) . ($extra ? " $extra" : ''); + $troff = _quote_macro_argument($troff) . ($extra ? " $extra" : ''); if ($nroff ne $troff) { return ".ie n $command $nroff\n.el $command $troff\n"; } else { return "$command $nroff\n"; } } else { - $text = qq("$text") . ($extra ? " $extra" : ''); + $text = _quote_macro_argument($text) . ($extra ? " $extra" : ''); return "$command $text\n"; } } @@ -761,10 +821,22 @@ sub outindex { # Output some text, without any additional changes. sub output { my ($self, @text) = @_; - if ($$self{ENCODE}) { - print { $$self{output_fh} } Encode::encode ('UTF-8', join ('', @text)); + my $text = join('', @text); + $text =~ s{$NBSP}{\\ }g; + $text =~ s{$SHY}{\\%}g; + + if ($$self{ENCODE} && _needs_encode($$self{ENCODING})) { + my $check = sub { + my ($char) = @_; + my $display = '"\x{' . hex($char) . '}"'; + my $error = "$display does not map to $$self{ENCODING}"; + $self->whine ($self->line_count(), $error); + return Encode::encode ($$self{ENCODING}, chr($char)); + }; + my $output = Encode::encode ($$self{ENCODING}, $text, $check); + print { $$self{output_fh} } $output; } else { - print { $$self{output_fh} } @text; + print { $$self{output_fh} } $text; } } @@ -777,21 +849,20 @@ sub output { sub start_document { my ($self, $attrs) = @_; if ($$attrs{contentless} && !$$self{ALWAYS_EMIT_SOMETHING}) { - DEBUG and print "Document is contentless\n"; $$self{CONTENTLESS} = 1; } else { delete $$self{CONTENTLESS}; } - # When UTF-8 output is set, check whether our output file handle already - # has a PerlIO encoding layer set. If it does not, we'll need to encode - # our output before printing it (handled in the output() sub). Wrap the - # check in an eval to handle versions of Perl without PerlIO. + # When an encoding is requested, check whether our output file handle + # already has a PerlIO encoding layer set. If it does not, we'll need to + # encode our output before printing it (handled in the output() sub). + # Wrap the check in an eval to handle versions of Perl without PerlIO. # # PerlIO::get_layers still requires its argument be a glob, so coerce the # file handle to a glob. $$self{ENCODE} = 0; - if ($$self{utf8}) { + if ($$self{ENCODING}) { $$self{ENCODE} = 1; eval { my @options = (output => 1, details => 1); @@ -806,15 +877,15 @@ sub start_document { # document was content-free. if (!$$self{CONTENTLESS}) { my ($name, $section); - if (defined $$self{name}) { - $name = $$self{name}; - $section = $$self{section} || 1; + if (defined $self->{opt_name}) { + $name = $self->{opt_name}; + $section = $self->{opt_section} || 1; } else { ($name, $section) = $self->devise_title; } - my $date = defined($$self{date}) ? $$self{date} : $self->devise_date; + my $date = $self->{opt_date} // $self->devise_date(); $self->preamble ($name, $section, $date) - unless $self->bare_output or DEBUG > 9; + unless $self->bare_output; } # Initialize a few per-document variables. @@ -839,7 +910,6 @@ sub end_document { } return if $self->bare_output; return if ($$self{CONTENTLESS} && !$$self{ALWAYS_EMIT_SOMETHING}); - $self->output (q(.\" [End document]) . "\n") if DEBUG; } # Try to figure out the name and section from the file name and return them as @@ -848,8 +918,8 @@ sub end_document { sub devise_title { my ($self) = @_; my $name = $self->source_filename || ''; - my $section = $$self{section} || 1; - $section = 3 if (!$$self{section} && $name =~ /\.pm\z/i); + my $section = $self->{opt_section} || 1; + $section = 3 if (!$self->{opt_section} && $name =~ /\.pm\z/i); $name =~ s/\.p(od|[lm])\z//i; # If Pod::Parser gave us an IO::File reference as the source file name, @@ -981,34 +1051,53 @@ sub devise_date { # module, but this order is correct for both Solaris and Linux. sub preamble { my ($self, $name, $section, $date) = @_; - my $preamble = $self->preamble_template (!$$self{utf8}); + my $preamble = $self->preamble_template(); - # Build the index line and make sure that it will be syntactically valid. - my $index = "$name $section"; - $index =~ s/\"/\"\"/g; - - # If name or section contain spaces, quote them (section really never - # should, but we may as well be cautious). - for ($name, $section) { - if (/\s/) { - s/\"/\"\"/g; - $_ = '"' . $_ . '"'; + # groff's preconv script will use this line to correctly determine the + # input encoding if the encoding is one of the ones it recognizes. It + # must be the first or second line. + # + # If the output encoding is some version of Unicode, we could also add a + # Unicode Byte Order Mark to the start of the file, but the BOM is now + # deprecated and I am concerned that may break a *roff implementation that + # might otherwise cope with Unicode. Revisit this if someone files a bug + # report about it. + if (_needs_encode($$self{ENCODING})) { + my $normalized = lc($$self{ENCODING}); + $normalized =~ s{-}{}g; + my $coding = $ENCODINGS{$normalized} || lc($$self{ENCODING}); + if ($coding ne 'us-ascii') { + $self->output(qq{.\\\" -*- mode: troff; coding: $coding -*-\n}); } } - # Double quotes in date, since it will be quoted. - $date =~ s/\"/\"\"/g; - - # Substitute into the preamble the configuration options. - $preamble =~ s/\@CFONT\@/$$self{fixed}/; - $preamble =~ s/\@LQUOTE\@/$$self{LQUOTE}/; - $preamble =~ s/\@RQUOTE\@/$$self{RQUOTE}/; - chomp $preamble; + # Substitute into the preamble the configuration options. Because it's + # used as the argument to defining a string, any leading double quote (but + # no other double quotes) in LQUOTE and RQUOTE has to be doubled. + $preamble =~ s{ [@] CFONT [@] }{$self->{opt_fixed}}xms; + my $lquote = $self->{LQUOTE}; + my $rquote = $self->{RQUOTE}; + $lquote =~ s{ \A \" }{""}xms; + $rquote =~ s{ \A \" }{""}xms; + $preamble =~ s{ [@] LQUOTE [@] }{$lquote}xms; + $preamble =~ s{ [@] RQUOTE [@] }{$rquote}xms; + chomp($preamble); # Get the version information. - my $version = $self->version_report; + my $version = $self->version_report(); - # Finally output everything. + # Build the index line and make sure that it will be syntactically valid. + my $index = _quote_macro_argument("$name $section"); + + # Quote the arguments to the .TH macro. (Section should never require + # this, but we may as well be cautious.) + $name = _quote_macro_argument($name); + $section = _quote_macro_argument($section); + $date = _quote_macro_argument($date); + my $center = _quote_macro_argument($self->{opt_center}); + my $release = _quote_macro_argument($self->{opt_release}); + + # Output the majority of the preamble. $self->output (<<"----END OF HEADER----"); .\\" Automatically generated by $version .\\" @@ -1017,14 +1106,19 @@ sub preamble { $preamble .\\" ======================================================================== .\\" -.IX Title "$index" -.TH $name $section "$date" "$$self{release}" "$$self{center}" +.IX Title $index +.TH $name $section $date $release $center .\\" For nroff, turn off justification. Always turn off hyphenation; it makes .\\" way too many mistakes in technical documents. .if n .ad l .nh ----END OF HEADER---- - $self->output (".\\\" [End of preamble]\n") if DEBUG; + + # If the language was specified, output the language configuration. + if ($self->{opt_language}) { + $self->output(".mso $self->{opt_language}.tmac\n"); + $self->output(".hla $self->{opt_language}\n"); + } } ############################################################################## @@ -1049,11 +1143,6 @@ sub cmd_para { $$self{SHIFTWAIT} = 0; } - # Add the line number for debugging, but not in the NAME section just in - # case the comment would confuse apropos. - $self->output (".\\\" [At source line $line]\n") - if defined ($line) && DEBUG && !$$self{IN_NAME}; - # Force exactly one newline at the end and strip unwanted trailing # whitespace at the end, but leave "\ " backslashed space from an S< > at # the end of a line. Reverse the text first, to avoid having to scan the @@ -1063,8 +1152,8 @@ sub cmd_para { $text = reverse $text; # Output the paragraph. - $self->output ($self->protect ($self->textmapfonts ($text))); - $self->outindex; + $self->output($self->protect($self->mapfonts($text, '\fR'))); + $self->outindex(); $$self{NEEDSPACE} = 1; return ''; } @@ -1076,45 +1165,44 @@ sub cmd_verbatim { my ($self, $attrs, $text) = @_; # Ignore an empty verbatim paragraph. - return unless $text =~ /\S/; + return if $text !~ m{ \S }xms; # Force exactly one newline at the end and strip unwanted trailing - # whitespace at the end. Reverse the text first, to avoid having to scan - # the entire paragraph. - $text = reverse $text; - $text =~ s/\A\s*/\n/; - $text = reverse $text; + # whitespace at the end. + $text =~ s{ \s* \z }{\n}xms; # Get a count of the number of lines before the first blank line, which # we'll pass to .Vb as its parameter. This tells *roff to keep that many # lines together. We don't want to tell *roff to keep huge blocks # together. - my @lines = split (/\n/, $text); + my @lines = split (m{ \n }xms, $text); my $unbroken = 0; - for (@lines) { - last if /^\s*$/; + for my $line (@lines) { + last if $line =~ m{ \A \s* \z }xms; $unbroken++; } - $unbroken = 10 if ($unbroken > 12 && !$$self{MAGIC_VNOPAGEBREAK_LIMIT}); + if ($unbroken > 12) { + $unbroken = 10; + } - # Prepend a null token to each line. - $text =~ s/^/\\&/gm; + # Prepend a null token to each line to preserve indentation. + $text =~ s{ ^ }{\\&}xmsg; # Output the results. - $self->makespace; - $self->output (".Vb $unbroken\n$text.Ve\n"); + $self->makespace(); + $self->output(".Vb $unbroken\n$text.Ve\n"); $$self{NEEDSPACE} = 1; - return ''; + return q{}; } # Handle literal text (produced by =for and similar constructs). Just output # it with the minimum of changes. sub cmd_data { my ($self, $attrs, $text) = @_; - $text =~ s/^\n+//; - $text =~ s/\n{0,2}$/\n/; - $self->output ($text); - return ''; + $text =~ s{ \A \n+ }{}xms; + $text =~ s{ \n{0,2} \z }{\n}xms; + $self->output($text); + return q{}; } ############################################################################## @@ -1136,9 +1224,6 @@ sub heading_common { $self->output (".PD\n"); } - # Output the current source line. - $self->output ( ".\\\" [At source line $line]\n" ) - if defined ($line) && DEBUG; return $text; } @@ -1151,7 +1236,7 @@ sub cmd_head1 { $text =~ s/\\s-?\d//g; $text = $self->heading_common ($text, $$attrs{start_line}); my $isname = ($text eq 'NAME' || $text =~ /\(NAME\)/); - $self->output ($self->switchquotes ('.SH', $self->mapfonts ($text))); + $self->output($self->switchquotes('.SH', $self->mapfonts($text, '\fP'))); $self->outindex ('Header', $text) unless $isname; $$self{NEEDSPACE} = 0; $$self{IN_NAME} = $isname; @@ -1162,7 +1247,7 @@ sub cmd_head1 { sub cmd_head2 { my ($self, $attrs, $text) = @_; $text = $self->heading_common ($text, $$attrs{start_line}); - $self->output ($self->switchquotes ('.SS', $self->mapfonts ($text))); + $self->output($self->switchquotes('.SS', $self->mapfonts($text, '\fP'))); $self->outindex ('Subsection', $text); $$self{NEEDSPACE} = 0; return ''; @@ -1174,7 +1259,7 @@ sub cmd_head3 { my ($self, $attrs, $text) = @_; $text = $self->heading_common ($text, $$attrs{start_line}); $self->makespace; - $self->output ($self->textmapfonts ('\f(IS' . $text . '\f(IE') . "\n"); + $self->output($self->mapfonts('\f(IS' . $text . '\f(IE', '\fR') . "\n"); $self->outindex ('Subsection', $text); $$self{NEEDSPACE} = 1; return ''; @@ -1186,7 +1271,7 @@ sub cmd_head4 { my ($self, $attrs, $text) = @_; $text = $self->heading_common ($text, $$attrs{start_line}); $self->makespace; - $self->output ($self->textmapfonts ($text) . "\n"); + $self->output($self->mapfonts($text, '\fR') . "\n"); $self->outindex ('Subsection', $text); $$self{NEEDSPACE} = 1; return ''; @@ -1203,6 +1288,13 @@ sub cmd_i { return $_[0]->{IN_NAME} ? $_[2] : '\f(IS' . $_[2] . '\f(IE' } sub cmd_f { return $_[0]->{IN_NAME} ? $_[2] : '\f(IS' . $_[2] . '\f(IE' } sub cmd_c { return $_[0]->quote_literal ($_[2]) } +# Convert all internal whitespace to $NBSP. +sub cmd_s { + my ($self, $attrs, $text) = @_; + $text =~ s{ \s }{$NBSP}xmsg; + return $text; +} + # Index entries are just added to the pending entries. sub cmd_x { my ($self, $attrs, $text) = @_; @@ -1224,7 +1316,7 @@ sub cmd_l { } if (not defined ($to) or $to eq $text) { return "<$text>"; - } elsif ($$self{nourls}) { + } elsif ($self->{opt_nourls}) { return $text; } else { return "$text <$$attrs{to}>"; @@ -1245,12 +1337,10 @@ sub over_common_start { my ($self, $type, $attrs) = @_; my $line = $$attrs{start_line}; my $indent = $$attrs{indent}; - DEBUG > 3 and print " Starting =over $type (line $line, indent ", - ($indent || '?'), "\n"; # Find the indentation level. unless (defined ($indent) && $indent =~ /^[-+]?\d{1,4}\s*$/) { - $indent = $$self{indent}; + $indent = $self->{opt_indent}; } # If we've gotten multiple indentations in a row, we need to emit the @@ -1279,7 +1369,6 @@ sub over_common_start { # .RE and then a new .RS to unconfuse *roff. sub over_common_end { my ($self) = @_; - DEBUG > 3 and print " Ending =over\n"; $$self{INDENT} = pop @{ $$self{INDENTS} }; pop @{ $$self{ITEMTYPES} }; @@ -1318,7 +1407,6 @@ sub end_over_block { $_[0]->over_common_end } sub item_common { my ($self, $type, $attrs, $text) = @_; my $line = $$attrs{start_line}; - DEBUG > 3 and print " $type item (line $line): $text\n"; # Clean up the text. We want to end up with two variables, one ($text) # which contains any body text after taking out the item portion, and @@ -1350,8 +1438,8 @@ sub item_common { $self->output (".PD 0\n") if ($$self{ITEMS} == 1); # Now, output the item tag itself. - $item = $self->textmapfonts ($item); - $self->output ($self->switchquotes ('.IP', $item, $$self{INDENT})); + $item = $self->mapfonts($item, '\fR'); + $self->output($self->switchquotes('.IP', $item, $$self{INDENT})); $$self{NEEDSPACE} = 0; $$self{ITEMS}++; $$self{SHIFTWAIT} = 0; @@ -1360,7 +1448,7 @@ sub item_common { if ($text) { $text =~ s/\s*$/\n/; $self->makespace; - $self->output ($self->protect ($self->textmapfonts ($text))); + $self->output($self->protect($self->mapfonts($text, '\fR'))); $$self{NEEDSPACE} = 1; } $self->outindex ($index ? ('Item', $index) : ()); @@ -1448,48 +1536,15 @@ sub parse_string_document { } ############################################################################## -# Translation tables -############################################################################## - -# The following table is adapted from Tom Christiansen's pod2man. It assumes -# that the standard preamble has already been printed, since that's what -# defines all of the accent marks. We really want to do something better than -# this when *roff actually supports other character sets itself, since these -# results are pretty poor. -# -# This only works in an ASCII world. What to do in a non-ASCII world is very -# unclear -- hopefully we can assume UTF-8 and just leave well enough alone. -@ESCAPES{0xA0 .. 0xFF} = ( - "\\ ", undef, undef, undef, undef, undef, undef, undef, - undef, undef, undef, undef, undef, "\\%", undef, undef, - - undef, undef, undef, undef, undef, undef, undef, undef, - undef, undef, undef, undef, undef, undef, undef, undef, - - "A\\*`", "A\\*'", "A\\*^", "A\\*~", "A\\*:", "A\\*o", "\\*(Ae", "C\\*,", - "E\\*`", "E\\*'", "E\\*^", "E\\*:", "I\\*`", "I\\*'", "I\\*^", "I\\*:", - - "\\*(D-", "N\\*~", "O\\*`", "O\\*'", "O\\*^", "O\\*~", "O\\*:", undef, - "O\\*/", "U\\*`", "U\\*'", "U\\*^", "U\\*:", "Y\\*'", "\\*(Th", "\\*8", - - "a\\*`", "a\\*'", "a\\*^", "a\\*~", "a\\*:", "a\\*o", "\\*(ae", "c\\*,", - "e\\*`", "e\\*'", "e\\*^", "e\\*:", "i\\*`", "i\\*'", "i\\*^", "i\\*:", - - "\\*(d-", "n\\*~", "o\\*`", "o\\*'", "o\\*^", "o\\*~", "o\\*:", undef, - "o\\*/" , "u\\*`", "u\\*'", "u\\*^", "u\\*:", "y\\*'", "\\*(th", "y\\*:", -) if ASCII; - -############################################################################## # Premable ############################################################################## -# The following is the static preamble which starts all *roff output we -# generate. Most is static except for the font to use as a fixed-width font, -# which is designed by @CFONT@, and the left and right quotes to use for C<> -# text, designated by @LQOUTE@ and @RQUOTE@. However, the second part, which -# defines the accent marks, is only used if $escapes is set to true. +# The preamble which starts all *roff output we generate. Most is static +# except for the font to use as a fixed-width font (designed by @CFONT@), and +# the left and right quotes to use for C<> text (designated by @LQOUTE@ and +# @RQUOTE@). Accent marks are only defined if the output encoding is roff. sub preamble_template { - my ($self, $accents) = @_; + my ($self) = @_; my $preamble = <<'----END OF PREAMBLE----'; .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v @@ -1504,29 +1559,12 @@ sub preamble_template { .ft R .fi .. -.\" Set up some character translations and predefined strings. \*(-- will -.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left -.\" double quote, and \*(R" will give a right double quote. \*(C+ will -.\" give a nicer C++. Capital omega is used to do unbreakable dashes and -.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, -.\" nothing in troff, for use with C<>. -.tr \(*W- -.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' +.\" \*(C` and \*(C' are quotes in nroff, nothing in troff, for use with C<>. .ie n \{\ -. ds -- \(*W- -. ds PI pi -. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch -. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch -. ds L" "" -. ds R" "" . ds C` @LQUOTE@ . ds C' @RQUOTE@ 'br\} .el\{\ -. ds -- \|\(em\| -. ds PI \(*p -. ds L" `` -. ds R" '' . ds C` . ds C' 'br\} @@ -1560,7 +1598,7 @@ sub preamble_template { ----END OF PREAMBLE---- #'# for cperl-mode - if ($accents) { + if ($$self{ENCODING} eq 'roff') { $preamble .= <<'----END OF PREAMBLE----' .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). @@ -1590,7 +1628,7 @@ sub preamble_template { . ds / .\} .if t \{\ -. ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" +. ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h'|\\n:u' . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' @@ -1637,10 +1675,13 @@ sub preamble_template { 1; __END__ +=encoding UTF-8 + =for stopwords en em ALLCAPS teeny fixedbold fixeditalic fixedbolditalic stderr utf8 UTF-8 Allbery Sean Burke Ossanna Solaris formatters troff uppercased Christiansen -nourls parsers Kernighan lquote rquote +nourls parsers Kernighan lquote rquote unrepresentable mandoc NetBSD PostScript +SMP macOS EBCDIC fallbacks manref reflowed reflowing FH overridable =head1 NAME @@ -1666,100 +1707,197 @@ using L<nroff(1)>, normally via L<man(1)>, or printing using L<troff(1)>. It is conventionally invoked using the driver script B<pod2man>, but it can also be used directly. -As a derived class from Pod::Simple, Pod::Man supports the same methods and -interfaces. See L<Pod::Simple> for all the details. - -new() can take options, in the form of key/value pairs that control the -behavior of the parser. See below for details. - -If no options are given, Pod::Man uses the name of the input file with any -trailing C<.pod>, C<.pm>, or C<.pl> stripped as the man page title, to -section 1 unless the file ended in C<.pm> in which case it defaults to -section 3, to a centered title of "User Contributed Perl Documentation", to -a centered footer of the Perl version it is run with, and to a left-hand -footer of the modification date of its input (or the current date if given -C<STDIN> for input). - -Pod::Man assumes that your *roff formatters have a fixed-width font named -C<CW>. If yours is called something else (like C<CR>), use the C<fixed> -option to specify it. This generally only matters for troff output for -printing. Similarly, you can set the fonts used for bold, italic, and -bold italic fixed-width output. - -Besides the obvious pod conversions, Pod::Man also takes care of -formatting func(), func(3), and simple variable references like $foo or -@bar so you don't have to use code escapes for them; complex expressions -like C<$fred{'stuff'}> will still need to be escaped, though. It also -translates dashes that aren't used as hyphens into en dashes, makes long -dashes--like this--into proper em dashes, fixes "paired quotes," makes C++ -look right, puts a little space between double underscores, makes ALLCAPS -a teeny bit smaller in B<troff>, and escapes stuff that *roff treats as -special so that you don't have to. - -The recognized options to new() are as follows. All options take a single -argument. +By default (on non-EBCDIC systems), Pod::Man outputs UTF-8. Its output should +work with the B<man> program on systems that use B<groff> (most Linux +distributions) or B<mandoc> (most BSD variants), but may result in mangled +output on older UNIX systems. To choose a different, possibly more +backward-compatible output mangling on such systems, set the C<encoding> +option to C<roff> (the default in earlier Pod::Man versions). See the +C<encoding> option and L</ENCODING> for more details. + +See L</COMPATIBILTY> for the versions of Pod::Man with significant +backward-incompatible changes (other than constructor options, whose versions +are documented below), and the versions of Perl that included them. + +=head1 CLASS METHODS + +=over 4 + +=item new(ARGS) + +Create a new Pod::Man object. ARGS should be a list of key/value pairs, where +the keys are chosen from the following. Each option is annotated with the +version of Pod::Man in which that option was added with its current meaning. =over 4 =item center -Sets the centered page header for the C<.TH> macro. The default, if this -option is not specified, is "User Contributed Perl Documentation". +[1.00] Sets the centered page header for the C<.TH> macro. The default, if +this option is not specified, is C<User Contributed Perl Documentation>. =item date -Sets the left-hand footer for the C<.TH> macro. If this option is not set, -the contents of the environment variable POD_MAN_DATE, if set, will be used. -Failing that, the value of SOURCE_DATE_EPOCH, the modification date of the -input file, or the current time if stat() can't find that file (which will be -the case if the input is from C<STDIN>) will be used. If obtained from the -file modification date or the current time, the date will be formatted as -C<YYYY-MM-DD> and will be based on UTC (so that the output will be -reproducible regardless of local time zone). +[4.00] Sets the left-hand footer for the C<.TH> macro. If this option is not +set, the contents of the environment variable POD_MAN_DATE, if set, will be +used. Failing that, the value of SOURCE_DATE_EPOCH, the modification date of +the input file, or the current time if stat() can't find that file (which will +be the case if the input is from C<STDIN>) will be used. If taken from any +source other than POD_MAN_DATE (which is used verbatim), the date will be +formatted as C<YYYY-MM-DD> and will be based on UTC (so that the output will +be reproducible regardless of local time zone). + +=item encoding + +[5.00] Specifies the encoding of the output. The value must be an encoding +recognized by the L<Encode> module (see L<Encode::Supported>), or the special +values C<roff> or C<groff>. The default on non-EBCDIC systems is UTF-8. + +If the output contains characters that cannot be represented in this encoding, +that is an error that will be reported as configured by the C<errors> option. +If error handling is other than C<die>, the unrepresentable character will be +replaced with the Encode substitution character (normally C<?>). + +If the C<encoding> option is set to the special value C<groff> (the default on +EBCDIC systems), or if the Encode module is not available and the encoding is +set to anything other than C<roff>, Pod::Man will translate all non-ASCII +characters to C<\[uNNNN]> Unicode escapes. These are not traditionally part +of the *roff language, but are supported by B<groff> and B<mandoc> and thus by +the majority of manual page processors in use today. + +If the C<encoding> option is set to the special value C<roff>, Pod::Man will +do its historic transformation of (some) ISO 8859-1 characters into *roff +escapes that may be adequate in troff and may be readable (if ugly) in nroff. +This was the default behavior of versions of Pod::Man before 5.00. With this +encoding, all other non-ASCII characters will be replaced with C<X>. It may +be required for very old troff and nroff implementations that do not support +UTF-8, but its representation of any non-ASCII character is very poor and +often specific to European languages. + +If the output file handle has a PerlIO encoding layer set, setting C<encoding> +to anything other than C<groff> or C<roff> will be ignored and no encoding +will be done by Pod::Man. It will instead rely on the encoding layer to make +whatever output encoding transformations are desired. + +WARNING: The input encoding of the POD source is independent from the output +encoding, and setting this option does not affect the interpretation of the +POD input. Unless your POD source is US-ASCII, its encoding should be +declared with the C<=encoding> command in the source. If this is not done, +Pod::Simple will will attempt to guess the encoding and may be successful if +it's Latin-1 or UTF-8, but it will produce warnings. See L<perlpod(1)> for +more information. =item errors -How to report errors. C<die> says to throw an exception on any POD -formatting error. C<stderr> says to report errors on standard error, but -not to throw an exception. C<pod> says to include a POD ERRORS section -in the resulting documentation summarizing the errors. C<none> ignores -POD errors entirely, as much as possible. +[2.27] How to report errors. C<die> says to throw an exception on any POD +formatting error. C<stderr> says to report errors on standard error, but not +to throw an exception. C<pod> says to include a POD ERRORS section in the +resulting documentation summarizing the errors. C<none> ignores POD errors +entirely, as much as possible. The default is C<pod>. =item fixed -The fixed-width font to use for verbatim text and code. Defaults to -C<CW>. Some systems may want C<CR> instead. Only matters for B<troff> -output. +[1.00] The fixed-width font to use for verbatim text and code. Defaults to +C<CW>. Some systems prefer C<CR> instead. Only matters for B<troff> output. =item fixedbold -Bold version of the fixed-width font. Defaults to C<CB>. Only matters +[1.00] Bold version of the fixed-width font. Defaults to C<CB>. Only matters for B<troff> output. =item fixeditalic -Italic version of the fixed-width font (actually, something of a misnomer, -since most fixed-width fonts only have an oblique version, not an italic -version). Defaults to C<CI>. Only matters for B<troff> output. +[1.00] Italic version of the fixed-width font (something of a misnomer, since +most fixed-width fonts only have an oblique version, not an italic version). +Defaults to C<CI>. Only matters for B<troff> output. =item fixedbolditalic -Bold italic (probably actually oblique) version of the fixed-width font. -Pod::Man doesn't assume you have this, and defaults to C<CB>. Some -systems (such as Solaris) have this font available as C<CX>. Only matters -for B<troff> output. +[1.00] Bold italic (in theory, probably oblique in practice) version of the +fixed-width font. Pod::Man doesn't assume you have this, and defaults to +C<CB>. Some systems (such as Solaris) have this font available as C<CX>. +Only matters for B<troff> output. + +=item guesswork + +[5.00] By default, Pod::Man applies some default formatting rules based on +guesswork and regular expressions that are intended to make writing Perl +documentation easier and require less explicit markup. These rules may not +always be appropriate, particularly for documentation that isn't about Perl. +This option allows turning all or some of it off. + +The special value C<all> enables all guesswork. This is also the default for +backward compatibility reasons. The special value C<none> disables all +guesswork. Otherwise, the value of this option should be a comma-separated +list of one or more of the following keywords: + +=over 4 + +=item functions + +Convert function references like C<foo()> to bold even if they have no markup. +The function name accepts valid Perl characters for function names (including +C<:>), and the trailing parentheses must be present and empty. + +=item manref + +Make the first part (before the parentheses) of manual page references like +C<foo(1)> bold even if they have no markup. The section must be a single +number optionally followed by lowercase letters. + +=item quoting + +If no guesswork is enabled, any text enclosed in CZ<><> is surrounded by +double quotes in nroff (terminal) output unless the contents are already +quoted. When this guesswork is enabled, quote marks will also be suppressed +for Perl variables, function names, function calls, numbers, and hex +constants. + +=item variables + +Convert Perl variable names to a fixed-width font even if they have no markup. +This transformation will only be apparent in troff output, or some other +output format (unlike nroff terminal output) that supports fixed-width fonts. + +=back + +Any unknown guesswork name is silently ignored (for potential future +compatibility), so be careful about spelling. + +=item language + +[5.00] Add commands telling B<groff> that the input file is in the given +language. The value of this setting must be a language abbreviation for which +B<groff> provides supplemental configuration, such as C<ja> (for Japanese) or +C<zh> (for Chinese). + +Specifically, this adds: + + .mso <language>.tmac + .hla <language> + +to the start of the file, which configure correct line breaking for the +specified language. Without these commands, groff may not know how to add +proper line breaks for Chinese and Japanese text if the manual page is +installed into the normal manual page directory, such as F</usr/share/man>. + +On many systems, this will be done automatically if the manual page is +installed into a language-specific manual page directory, such as +F</usr/share/man/zh_CN>. In that case, this option is not required. + +Unfortunately, the commands added with this option are specific to B<groff> +and will not work with other B<troff> and B<nroff> implementations. =item lquote =item rquote -Sets the quote marks used to surround CE<lt>> text. C<lquote> sets the -left quote mark and C<rquote> sets the right quote mark. Either may also -be set to the special value C<none>, in which case no quote mark is added -on that side of CE<lt>> text (but the font is still changed for troff -output). +[4.08] Sets the quote marks used to surround CE<lt>> text. C<lquote> sets the +left quote mark and C<rquote> sets the right quote mark. Either may also be +set to the special value C<none>, in which case no quote mark is added on that +side of CE<lt>> text (but the font is still changed for troff output). Also see the C<quotes> option, which can be used to set both quotes at once. If both C<quotes> and one of the other options is set, C<lquote> or C<rquote> @@ -1767,21 +1905,21 @@ overrides C<quotes>. =item name -Set the name of the manual page for the C<.TH> macro. Without this -option, the manual name is set to the uppercased base name of the file -being converted unless the manual section is 3, in which case the path is -parsed to see if it is a Perl module path. If it is, a path like -C<.../lib/Pod/Man.pm> is converted into a name like C<Pod::Man>. This -option, if given, overrides any automatic determination of the name. +[4.08] Set the name of the manual page for the C<.TH> macro. Without this +option, the manual name is set to the uppercased base name of the file being +converted unless the manual section is 3, in which case the path is parsed to +see if it is a Perl module path. If it is, a path like C<.../lib/Pod/Man.pm> +is converted into a name like C<Pod::Man>. This option, if given, overrides +any automatic determination of the name. If generating a manual page from standard input, the name will be set to -C<STDIN> if this option is not provided. Providing this option is strongly -recommended to set a meaningful manual page name. +C<STDIN> if this option is not provided. In this case, providing this option +is strongly recommended to set a meaningful manual page name. =item nourls -Normally, LZ<><> formatting codes with a URL but anchor text are formatted -to show both the anchor text and the URL. In other words: +[2.27] Normally, LZ<><> formatting codes with a URL but anchor text are +formatted to show both the anchor text and the URL. In other words: L<foo|http://example.com/> @@ -1796,10 +1934,10 @@ important. =item quotes -Sets the quote marks used to surround CE<lt>> text. If the value is a -single character, it is used as both the left and right quote. Otherwise, -it is split in half, and the first half of the string is used as the left -quote and the second is used as the right quote. +[4.00] Sets the quote marks used to surround CE<lt>> text. If the value is a +single character, it is used as both the left and right quote. Otherwise, it +is split in half, and the first half of the string is used as the left quote +and the second is used as the right quote. This may also be set to the special value C<none>, in which case no quote marks are added around CE<lt>> text (but the font is still changed for troff @@ -1811,82 +1949,258 @@ options is set, C<lquote> or C<rquote> overrides C<quotes>. =item release -Set the centered footer for the C<.TH> macro. By default, this is set to -the version of Perl you run Pod::Man under. Setting this to the empty -string will cause some *roff implementations to use the system default -value. +[1.00] Set the centered footer for the C<.TH> macro. By default, this is set +to the version of Perl you run Pod::Man under. Setting this to the empty +string will cause some *roff implementations to use the system default value. -Note that some system C<an> macro sets assume that the centered footer -will be a modification date and will prepend something like "Last -modified: ". If this is the case for your target system, you may want to -set C<release> to the last modified date and C<date> to the version -number. +Note that some system C<an> macro sets assume that the centered footer will be +a modification date and will prepend something like C<Last modified: >. If +this is the case for your target system, you may want to set C<release> to the +last modified date and C<date> to the version number. =item section -Set the section for the C<.TH> macro. The standard section numbering -convention is to use 1 for user commands, 2 for system calls, 3 for -functions, 4 for devices, 5 for file formats, 6 for games, 7 for -miscellaneous information, and 8 for administrator commands. There is a lot -of variation here, however; some systems (like Solaris) use 4 for file -formats, 5 for miscellaneous information, and 7 for devices. Still others -use 1m instead of 8, or some mix of both. About the only section numbers -that are reliably consistent are 1, 2, and 3. +[1.00] Set the section for the C<.TH> macro. The standard section numbering +convention is to use 1 for user commands, 2 for system calls, 3 for functions, +4 for devices, 5 for file formats, 6 for games, 7 for miscellaneous +information, and 8 for administrator commands. There is a lot of variation +here, however; some systems (like Solaris) use 4 for file formats, 5 for +miscellaneous information, and 7 for devices. Still others use 1m instead of +8, or some mix of both. About the only section numbers that are reliably +consistent are 1, 2, and 3. By default, section 1 will be used unless the file ends in C<.pm> in which case section 3 will be selected. =item stderr -Send error messages about invalid POD to standard error instead of -appending a POD ERRORS section to the generated *roff output. This is -equivalent to setting C<errors> to C<stderr> if C<errors> is not already -set. It is supported for backward compatibility. +[2.19] If set to a true value, send error messages about invalid POD to +standard error instead of appending a POD ERRORS section to the generated +*roff output. This is equivalent to setting C<errors> to C<stderr> if +C<errors> is not already set. + +This option is for backward compatibility with Pod::Man versions that did not +support C<errors>. Normally, the C<errors> option should be used instead. =item utf8 -By default, Pod::Man produces the most conservative possible *roff output -to try to ensure that it will work with as many different *roff -implementations as possible. Many *roff implementations cannot handle -non-ASCII characters, so this means all non-ASCII characters are converted -either to a *roff escape sequence that tries to create a properly accented -character (at least for troff output) or to C<X>. - -If this option is set, Pod::Man will instead output UTF-8. If your *roff -implementation can handle it, this is the best output format to use and -avoids corruption of documents containing non-ASCII characters. However, -be warned that *roff source with literal UTF-8 characters is not supported -by many implementations and may even result in segfaults and other bad -behavior. - -Be aware that, when using this option, the input encoding of your POD -source should be properly declared unless it's US-ASCII. Pod::Simple will -attempt to guess the encoding and may be successful if it's Latin-1 or -UTF-8, but it will produce warnings. Use the C<=encoding> command to -declare the encoding. See L<perlpod(1)> for more information. +[2.21] This option used to set the output encoding to UTF-8. Since this is +now the default, it is ignored and does nothing. + +=back =back -The standard Pod::Simple method parse_file() takes one argument naming the -POD file to read from. By default, the output is sent to C<STDOUT>, but -this can be changed with the output_fh() method. +=head1 INSTANCE METHODS + +As a derived class from Pod::Simple, Pod::Man supports the same methods and +interfaces. See L<Pod::Simple> for all the details. This section summarizes +the most-frequently-used methods and the ones added by Pod::Man. + +=over 4 + +=item output_fh(FH) + +Direct the output from parse_file(), parse_lines(), or parse_string_document() +to the file handle FH instead of C<STDOUT>. + +=item output_string(REF) + +Direct the output from parse_file(), parse_lines(), or parse_string_document() +to the scalar variable pointed to by REF, rather than C<STDOUT>. For example: + + my $man = Pod::Man->new(); + my $output; + $man->output_string(\$output); + $man->parse_file('/some/input/file'); -The standard Pod::Simple method parse_from_file() takes up to two -arguments, the first being the input file to read POD from and the second -being the file to write the formatted output to. +Be aware that the output in that variable will already be encoded in UTF-8. -You can also call parse_lines() to parse an array of lines or -parse_string_document() to parse a document already in memory. As with -parse_file(), parse_lines() and parse_string_document() default to sending -their output to C<STDOUT> unless changed with the output_fh() method. Be -aware that parse_lines() and parse_string_document() both expect raw bytes, -not decoded characters. +=item parse_file(PATH) -To put the output from any parse method into a string instead of a file -handle, call the output_string() method instead of output_fh(). +Read the POD source from PATH and format it. By default, the output is sent +to C<STDOUT>, but this can be changed with the output_fh() or output_string() +methods. -See L<Pod::Simple> for more specific details on the methods available to -all derived parsers. +=item parse_from_file(INPUT, OUTPUT) + +=item parse_from_filehandle(FH, OUTPUT) + +Read the POD source from INPUT, format it, and output the results to OUTPUT. + +parse_from_filehandle() is provided for backward compatibility with older +versions of Pod::Man. parse_from_file() should be used instead. + +=item parse_lines(LINES[, ...[, undef]]) + +Parse the provided lines as POD source, writing the output to either C<STDOUT> +or the file handle set with the output_fh() or output_string() methods. This +method can be called repeatedly to provide more input lines. An explicit +C<undef> should be passed to indicate the end of input. + +This method expects raw bytes, not decoded characters. + +=item parse_string_document(INPUT) + +Parse the provided scalar variable as POD source, writing the output to either +C<STDOUT> or the file handle set with the output_fh() or output_string() +methods. + +This method expects raw bytes, not decoded characters. + +=back + +=head1 ENCODING + +As of Pod::Man 5.00, the default output encoding for Pod::Man is UTF-8. This +should work correctly on any modern system that uses either B<groff> (most +Linux distributions) or B<mandoc> (Alpine Linux and most BSD variants, +including macOS). + +The user will probably have to use a UTF-8 locale to see correct output. This +may be done by default; if not, set the LANG or LC_CTYPE environment variables +to an appropriate local. The locale C<C.UTF-8> is available on most systems +if one wants correct output without changing the other things locales affect, +such as collation. + +The backward-compatible output format used in Pod::Man versions before 5.00 is +available by setting the C<encoding> option to C<roff>. This may produce +marginally nicer results on older UNIX versions that do not use B<groff> or +B<mandoc>, but none of the available options will correctly render Unicode +characters on those systems. + +Below are some additional details about how this choice was made and some +discussion of alternatives. + +=head2 History + +The default output encoding for Pod::Man has been a long-standing problem. +B<troff> and B<nroff> predate Unicode by a significant margin, and their +implementations for many UNIX systems reflect that legacy. It's common for +Unicode to not be supported in any form. + +Because of this, versions of Pod::Man prior to 5.00 maintained the highly +conservative output of the original pod2man, which output pure ASCII with +complex macros to simulate common western European accented characters when +processed with troff. The nroff output was awkward and sometimes incorrect, +and characters not used in western European scripts were replaced with C<X>. +This choice maximized backwards compatibility with B<man> and +B<nroff>/B<troff> implementations at the cost of incorrect rendering of many +POD documents, particularly those containing people's names. + +The modern implementations, B<groff> (used in most Linux distributions) and +B<mandoc> (used by most BSD variants), do now support Unicode. Other UNIX +systems often do not, but they're now a tiny minority of the systems people +use on a daily basis. It's increasingly common (for very good reasons) to use +Unicode characters for POD documents rather than using ASCII conversions of +people's names or avoiding non-English text, making the limitations in the old +output format more apparent. + +Four options have been proposed to fix this: + +=over 2 + +=item * + +Optionally support UTF-8 output but don't change the default. This is the +approach taken since Pod::Man 2.1.0, which added the C<utf8> option. Some +Pod::Man users use this option for better output on platforms known to support +Unicode, but since the defaults have not changed, people continued to +encounter (and file bug reports about) the poor default rendering. + +=item * + +Convert characters to troff C<\(xx> escapes. This requires maintaining a +large translation table and addresses only a tiny part of the problem, since +many Unicode characters have no standard troff name. B<groff> has the largest +list, but if one is willing to assume B<groff> is the formatter, the next +option is better. + +=item * + +Convert characters to groff C<\[uNNNN]> escapes. This is implemented as the +C<groff> encoding for those who want to use it, and is supported by both +B<groff> and B<mandoc>. However, it is no better than UTF-8 output for +portability to other implementations. See L</Testing results> for more +details. + +=item * + +Change the default output format to UTF-8 and ask those who want maximum +backward compatibility to explicitly select the old encoding. This fixes the +issue for most users at the cost of backwards compatibility. While the +rendering of non-ASCII characters is different on older systems that don't +support UTF-8, it's not always worse than the old output. + +=back + +Pod::Man 5.00 and later makes the last choice. This arguably produces worse +output when manual pages are formatted with B<troff> into PostScript or PDF, +but doing this is rare and normally manual, so the encoding can be changed in +those cases. The older output encoding is available by setting C<encoding> to +C<roff>. + +=head2 Testing results + +Here is the results of testing C<encoding> values of C<utf-8> and C<groff> on +various operating systems. The testing methodology was to create F<man/man1> +in the current directory, copy F<encoding.utf8> or F<encoding.groff> from the +podlators 5.00 distribution to F<man/man1/encoding.1>, and then run: + + LANG=C.UTF-8 MANPATH=$(pwd)/man man 1 encoding + +If the locale is not explicitly set to one that includes UTF-8, the Unicode +characters were usually converted to ASCII (by, for example, dropping an +accent) or deleted or replaced with C<< <?> >> if there was no conversion. + +Tested on 2022-09-25. Many thanks to the GCC Compile Farm project for access +to testing hosts. + + OS UTF-8 groff + ------------------ ------- ------- + AIX 7.1 no [1] no [2] + Alpine 3.15.0 yes yes + CentOS 7.9 yes yes + Debian 7 yes yes + FreeBSD 13.0 yes yes + NetBSD 9.2 yes yes + OpenBSD 7.1 yes yes + openSUSE Leap 15.4 yes yes + Solaris 10 yes no [2] + Solaris 11 no [3] no [3] + +I did not have access to a macOS system for testing, but since it uses +B<mandoc>, it's behavior is probably the same as the BSD hosts. + +Notes: + +=over 4 + +=item [1] + +Unicode characters were converted to one or two random ASCII characters +unrelated to the original character. + +=item [2] + +Unicode characters were shown as the body of the groff escape rather than the +indicated character (in other words, text like C<[u00EF]>). + +=item [3] + +Unicode characters were deleted entirely, as if they weren't there. Using +C<nroff -man> instead of B<man> to format the page showed the same results as +Solaris 10. Using C<groff -k -man -Tutf8> to format the page produced the +correct output. + +=back + +PostScript and PDF output using groff on a Debian 12 system do not support +combining accent marks or SMP characters due to a lack of support in the +default output font. + +Testing on additional platforms is welcome. Please let the author know if you +have additional results. =head1 DIAGNOSTICS @@ -1922,9 +2236,9 @@ option was set to C<die>. =item PERL_CORE -If set and Encode is not available, silently fall back to non-UTF-8 mode -without complaining to standard error. This environment variable is set -during Perl core builds, which build Encode after podlators. Encode is +If set and Encode is not available, silently fall back to an encoding of +C<groff> without complaining to standard error. This environment variable is +set during Perl core builds, which build Encode after podlators. Encode is expected to not (yet) be available in that case. =item POD_MAN_DATE @@ -1953,65 +2267,104 @@ reliable if this variable overrode the timestamp of the input file.) =back -=head1 BUGS +=head1 COMPATIBILITY -Encoding handling assumes that PerlIO is available and does not work -properly if it isn't. The C<utf8> option is therefore not supported -unless Perl is built with PerlIO support. +Pod::Man 1.02 (based on L<Pod::Parser>) was the first version included with +Perl, in Perl 5.6.0. -There is currently no way to turn off the guesswork that tries to format -unmarked text appropriately, and sometimes it isn't wanted (particularly -when using POD to document something other than Perl). Most of the work -toward fixing this has now been done, however, and all that's still needed -is a user interface. +The current API based on L<Pod::Simple> was added in Pod::Man 2.00. Pod::Man +2.04 was included in Perl 5.9.3, the first version of Perl to incorporate +those changes. This is the first version that correctly supports all modern +POD syntax. The parse_from_filehandle() method was re-added for backward +compatibility in Pod::Man 2.09, included in Perl 5.9.4. -The NAME section should be recognized specially and index entries emitted -for everything in that section. This would have to be deferred until the -next section, since extraneous things in NAME tends to confuse various man -page processors. Currently, no index entries are emitted for anything in -NAME. +Support for anchor text in LZ<><> links of type URL was added in Pod::Man +2.23, included in Perl 5.11.5. -Pod::Man doesn't handle font names longer than two characters. Neither do -most B<troff> implementations, but GNU troff does as an extension. It would -be nice to support as an option for those who want to use it. +parse_lines(), parse_string_document(), and parse_file() set a default output +file handle of C<STDOUT> if one was not already set as of Pod::Man 2.28, +included in Perl 5.19.5. + +Support for SOURCE_DATE_EPOCH and POD_MAN_DATE was added in Pod::Man 4.00, +included in Perl 5.23.7, and generated dates were changed to use UTC instead +of the local time zone. This is also the first release that aligned the +module version and the version of the podlators distribution. All modules +included in podlators, and the podlators distribution itself, share the same +version number from this point forward. -The preamble added to each output file is rather verbose, and most of it -is only necessary in the presence of non-ASCII characters. It would -ideally be nice if all of those definitions were only output if needed, -perhaps on the fly as the characters are used. +Pod::Man 4.10, included in Perl 5.27.8, changed the formatting for manual page +references and function names to bold instead of italic, following the current +Linux manual page standard. -Pod::Man is excessively slow. +Pod::Man 5.00 changed the default output encoding to UTF-8, overridable with +the new C<encoding> option. It also fixed problems with bold or italic +extending too far when used with CZ<><> escapes, and began converting Unicode +zero-width spaces (U+200B) to the C<\:> *roff escape. It also dropped +attempts to add subtle formatting corrections in the output that would only be +visible when typeset with B<troff>, which had previously been a significant +source of bugs. + +=head1 BUGS + +There are numerous bugs and language-specific assumptions in the nroff +fallbacks for accented characters in the C<roff> encoding. Since the point of +this encoding is backward compatibility with the output from earlier versions +of Pod::Man, and it is deprecated except when necessary to support old +systems, those bugs are unlikely to ever be fixed. + +Pod::Man doesn't handle font names longer than two characters. Neither do +most B<troff> implementations, but groff does as an extension. It would be +nice to support as an option for those who want to use it. =head1 CAVEATS -If Pod::Man is given the C<utf8> option, the encoding of its output file -handle will be forced to UTF-8 if possible, overriding any existing -encoding. This will be done even if the file handle is not created by -Pod::Man and was passed in from outside. This maintains consistency -regardless of PERL_UNICODE and other settings. +=head2 Sentence spacing + +Pod::Man copies the input spacing verbatim to the output *roff document. This +means your output will be affected by how B<nroff> generally handles sentence +spacing. + +B<nroff> dates from an era in which it was standard to use two spaces after +sentences, and will always add two spaces after a line-ending period (or +similar punctuation) when reflowing text. For example, the following input: + + =pod -The handling of hyphens and em dashes is somewhat fragile, and one may get -the wrong one under some circumstances. This should only matter for -B<troff> output. + One sentence. + Another sentence. -When and whether to use small caps is somewhat tricky, and Pod::Man doesn't -necessarily get it right. +will result in two spaces after the period when the text is reflowed. If you +use two spaces after sentences anyway, this will be consistent, although you +will have to be careful to not end a line with an abbreviation such as C<e.g.> +or C<Ms.>. Output will also be consistent if you use the *roff style guide +(and L<XKCD 1285|https://xkcd.com/1285/>) recommendation of putting a line +break after each sentence, although that will consistently produce two spaces +after each sentence, which may not be what you want. -Converting neutral double quotes to properly matched double quotes doesn't -work unless there are no formatting codes between the quote marks. This -only matters for troff output. +If you prefer one space after sentences (which is the more modern style), you +will unfortunately need to ensure that no line in the middle of a paragraph +ends in a period or similar sentence-ending paragraph. Otherwise, B<nroff> +will add a two spaces after that sentence when reflowing, and your output +document will have inconsistent spacing. + +=head2 Hyphens + +The handling of hyphens versus dashes is somewhat fragile, and one may get a +the wrong one under some circumstances. This will normally only matter for +line breaking and possibly for troff output. =head1 AUTHOR -Russ Allbery <rra@cpan.org>, based I<very> heavily on the original B<pod2man> -by Tom Christiansen <tchrist@mox.perl.com>. The modifications to work with -Pod::Simple instead of Pod::Parser were originally contributed by Sean Burke -<sburke@cpan.org> (but I've since hacked them beyond recognition and all bugs -are mine). +Written by Russ Allbery <rra@cpan.org>, based on the original B<pod2man> by +Tom Christiansen <tchrist@mox.perl.com>. + +The modifications to work with Pod::Simple instead of Pod::Parser were +contributed by Sean Burke <sburke@cpan.org>, but I've since hacked them beyond +recognition and all bugs are mine. =head1 COPYRIGHT AND LICENSE -Copyright 1999-2010, 2012-2019 Russ Allbery <rra@cpan.org> +Copyright 1999-2010, 2012-2020, 2022 Russ Allbery <rra@cpan.org> Substantial contributions by Sean Burke <sburke@cpan.org>. @@ -2020,18 +2373,19 @@ under the same terms as Perl itself. =head1 SEE ALSO -L<Pod::Simple>, L<perlpod(1)>, L<pod2man(1)>, L<nroff(1)>, L<troff(1)>, -L<man(1)>, L<man(7)> +L<Encode::Supported>, L<Pod::Simple>, L<perlpod(1)>, L<pod2man(1)>, +L<nroff(1)>, L<troff(1)>, L<man(1)>, L<man(7)> Ossanna, Joseph F., and Brian W. Kernighan. "Troff User's Manual," Computing Science Technical Report No. 54, AT&T Bell Laboratories. This is the best documentation of standard B<nroff> and B<troff>. At the time of this writing, it's available at L<http://www.troff.org/54.pdf>. -The man page documenting the man macro set may be L<man(5)> instead of -L<man(7)> on your system. Also, please see L<pod2man(1)> for extensive -documentation on writing manual pages if you've not done it before and -aren't familiar with the conventions. +The manual page documenting the man macro set may be L<man(5)> instead of +L<man(7)> on your system. + +See L<perlpodstyle(1)> for documentation on writing manual pages in POD if +you've not done it before and aren't familiar with the conventions. The current version of this module is always available from its web site at L<https://www.eyrie.org/~eagle/software/podlators/>. It is also part of the |