Spaces:
Running
Running
| # Convert POD data to ASCII text with format escapes. | |
| # | |
| # This is a simple subclass of Pod::Text that overrides a few key methods to | |
| # output the right termcap escape sequences for formatted text on the current | |
| # terminal type. | |
| # | |
| # SPDX-License-Identifier: GPL-1.0-or-later OR Artistic-1.0-Perl | |
| ############################################################################## | |
| # Modules and declarations | |
| ############################################################################## | |
| package Pod::Text::Termcap; | |
| use 5.008; | |
| use strict; | |
| use warnings; | |
| use Pod::Text (); | |
| use POSIX (); | |
| use Term::Cap; | |
| use vars qw(@ISA $VERSION); | |
| @ISA = qw(Pod::Text); | |
| $VERSION = '4.14'; | |
| ############################################################################## | |
| # Overrides | |
| ############################################################################## | |
| # In the initialization method, grab our terminal characteristics as well as | |
| # do all the stuff we normally do. | |
| sub new { | |
| my ($self, %args) = @_; | |
| my ($ospeed, $term, $termios); | |
| # Fall back on a hard-coded terminal speed if POSIX::Termios isn't | |
| # available (such as on VMS). | |
| eval { $termios = POSIX::Termios->new }; | |
| if ($@) { | |
| $ospeed = 9600; | |
| } else { | |
| $termios->getattr; | |
| $ospeed = $termios->getospeed || 9600; | |
| } | |
| # Get data from Term::Cap if possible. | |
| my ($bold, $undl, $norm, $width); | |
| eval { | |
| my $term = Tgetent Term::Cap { TERM => undef, OSPEED => $ospeed }; | |
| $bold = $term->Tputs('md'); | |
| $undl = $term->Tputs('us'); | |
| $norm = $term->Tputs('me'); | |
| if (defined $$term{_co}) { | |
| $width = $$term{_co}; | |
| $width =~ s/^\#//; | |
| } | |
| }; | |
| # Figure out the terminal width before calling the Pod::Text constructor, | |
| # since it will otherwise force 76 characters. Pod::Text::Termcap has | |
| # historically used 2 characters less than the width of the screen, while | |
| # the other Pod::Text classes have used 76. This is weirdly inconsistent, | |
| # but there's probably no good reason to change it now. | |
| unless (defined $args{width}) { | |
| $args{width} = $ENV{COLUMNS} || $width || 80; | |
| $args{width} -= 2; | |
| } | |
| # Initialize Pod::Text. | |
| $self = $self->SUPER::new (%args); | |
| # If we were unable to get any of the formatting sequences, don't attempt | |
| # that type of formatting. This will do weird things if bold or underline | |
| # were available but normal wasn't, but hopefully that will never happen. | |
| $$self{BOLD} = $bold || q{}; | |
| $$self{UNDL} = $undl || q{}; | |
| $$self{NORM} = $norm || q{}; | |
| return $self; | |
| } | |
| # Make level one headings bold. | |
| sub cmd_head1 { | |
| my ($self, $attrs, $text) = @_; | |
| $text =~ s/\s+$//; | |
| $self->SUPER::cmd_head1 ($attrs, "$$self{BOLD}$text$$self{NORM}"); | |
| } | |
| # Make level two headings bold. | |
| sub cmd_head2 { | |
| my ($self, $attrs, $text) = @_; | |
| $text =~ s/\s+$//; | |
| $self->SUPER::cmd_head2 ($attrs, "$$self{BOLD}$text$$self{NORM}"); | |
| } | |
| # Fix up B<> and I<>. Note that we intentionally don't do F<>. | |
| sub cmd_b { my $self = shift; return "$$self{BOLD}$_[1]$$self{NORM}" } | |
| sub cmd_i { my $self = shift; return "$$self{UNDL}$_[1]$$self{NORM}" } | |
| # Return a regex that matches a formatting sequence. This will only be valid | |
| # if we were able to get at least some termcap information. | |
| sub format_regex { | |
| my ($self) = @_; | |
| my @codes = ($self->{BOLD}, $self->{UNDL}, $self->{NORM}); | |
| return join(q{|}, map { $_ eq q{} ? () : "\Q$_\E" } @codes); | |
| } | |
| # Analyze a single line and return any formatting codes in effect at the end | |
| # of that line. | |
| sub end_format { | |
| my ($self, $line) = @_; | |
| my $pattern = "(" . $self->format_regex() . ")"; | |
| my $current; | |
| while ($line =~ /$pattern/g) { | |
| my $code = $1; | |
| if ($code eq $$self{NORM}) { | |
| undef $current; | |
| } else { | |
| $current .= $code; | |
| } | |
| } | |
| return $current; | |
| } | |
| # Output any included code in bold. | |
| sub output_code { | |
| my ($self, $code) = @_; | |
| $self->output ($$self{BOLD} . $code . $$self{NORM}); | |
| } | |
| # Strip all of the formatting from a provided string, returning the stripped | |
| # version. | |
| sub strip_format { | |
| my ($self, $text) = @_; | |
| $text =~ s/\Q$$self{BOLD}//g; | |
| $text =~ s/\Q$$self{UNDL}//g; | |
| $text =~ s/\Q$$self{NORM}//g; | |
| return $text; | |
| } | |
| # Override the wrapping code to ignore the special sequences. | |
| sub wrap { | |
| my $self = shift; | |
| local $_ = shift; | |
| my $output = ''; | |
| my $spaces = ' ' x $$self{MARGIN}; | |
| my $width = $$self{opt_width} - $$self{MARGIN}; | |
| # If we were unable to find any termcap sequences, use Pod::Text wrapping. | |
| if ($self->{BOLD} eq q{} && $self->{UNDL} eq q{} && $self->{NORM} eq q{}) { | |
| return $self->SUPER::wrap($_); | |
| } | |
| # $code matches a single special sequence. $char matches any number of | |
| # special sequences preceding a single character other than a newline. | |
| # $shortchar matches some sequence of $char ending in codes followed by | |
| # whitespace or the end of the string. $longchar matches exactly $width | |
| # $chars, used when we have to truncate and hard wrap. | |
| my $code = "(?:" . $self->format_regex() . ")"; | |
| my $char = "(?>$code*[^\\n])"; | |
| my $shortchar = '^(' . $char . "{0,$width}(?>$code*)" . ')(?:\s+|\z)'; | |
| my $longchar = '^(' . $char . "{$width})"; | |
| while (length > $width) { | |
| if (s/$shortchar// || s/$longchar//) { | |
| $output .= $spaces . $1 . "\n"; | |
| } else { | |
| last; | |
| } | |
| } | |
| $output .= $spaces . $_; | |
| # less -R always resets terminal attributes at the end of each line, so we | |
| # need to clear attributes at the end of lines and then set them again at | |
| # the start of the next line. This requires a second pass through the | |
| # wrapped string, accumulating any attributes we see, remembering them, | |
| # and then inserting the appropriate sequences at the newline. | |
| if ($output =~ /\n/) { | |
| my @lines = split (/\n/, $output); | |
| my $start_format; | |
| for my $line (@lines) { | |
| if ($start_format && $line =~ /\S/) { | |
| $line =~ s/^(\s*)(\S)/$1$start_format$2/; | |
| } | |
| $start_format = $self->end_format ($line); | |
| if ($start_format) { | |
| $line .= $$self{NORM}; | |
| } | |
| } | |
| $output = join ("\n", @lines); | |
| } | |
| # Fix up trailing whitespace and return the results. | |
| $output =~ s/\s+$/\n\n/; | |
| return $output; | |
| } | |
| ############################################################################## | |
| # Module return value and documentation | |
| ############################################################################## | |
| 1; | |
| __END__ | |
| =head1 NAME | |
| Pod::Text::Termcap - Convert POD data to ASCII text with format escapes | |
| =for stopwords | |
| ECMA-48 VT100 Allbery Solaris TERMPATH | |
| =head1 SYNOPSIS | |
| use Pod::Text::Termcap; | |
| my $parser = Pod::Text::Termcap->new (sentence => 0, width => 78); | |
| # Read POD from STDIN and write to STDOUT. | |
| $parser->parse_from_filehandle; | |
| # Read POD from file.pod and write to file.txt. | |
| $parser->parse_from_file ('file.pod', 'file.txt'); | |
| =head1 DESCRIPTION | |
| Pod::Text::Termcap is a simple subclass of Pod::Text that highlights output | |
| text using the correct termcap escape sequences for the current terminal. | |
| Apart from the format codes, it in all ways functions like Pod::Text. See | |
| L<Pod::Text> for details and available options. | |
| This module uses L<Term::Cap> to find the correct terminal settings. See the | |
| documentation of that module for how it finds terminal database information | |
| and how to override that behavior if necessary. If unable to find control | |
| strings for bold and underscore formatting, that formatting is skipped, | |
| resulting in the same output as Pod::Text. | |
| =head1 AUTHOR | |
| Russ Allbery <[email protected]> | |
| =head1 COPYRIGHT AND LICENSE | |
| Copyright 1999, 2001-2002, 2004, 2006, 2008-2009, 2014-2015, 2018-2019 Russ | |
| Allbery <[email protected]> | |
| This program is free software; you may redistribute it and/or modify it | |
| under the same terms as Perl itself. | |
| =head1 SEE ALSO | |
| L<Pod::Text>, L<Pod::Simple>, L<Term::Cap> | |
| 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 | |
| Perl core distribution as of 5.6.0. | |
| =cut | |
| # Local Variables: | |
| # copyright-at-end-flag: t | |
| # End: | |