| # Texinfo.pm: format Pod as Texinfo. |
| # |
| # Copyright 2011, 2012, 2013, 2014 Free Software Foundation, Inc. |
| # |
| # This program is free software; you can redistribute it and/or modify |
| # it under the terms of the GNU General Public License as published by |
| # the Free Software Foundation; either version 3 of the License, |
| # or (at your option) any later version. |
| # |
| # This program is distributed in the hope that it will be useful, |
| # but WITHOUT ANY WARRANTY; without even the implied warranty of |
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| # GNU General Public License for more details. |
| # |
| # You should have received a copy of the GNU General Public License |
| # along with this program. If not, see <http://www.gnu.org/licenses/>. |
| # |
| # Original author: Patrice Dumas <pertusus@free.fr> |
| # Parts from L<Pod::Simple::HTML>. |
| |
| |
| package Pod::Simple::Texinfo; |
| |
| require 5; |
| use strict; |
| |
| use Carp qw(cluck); |
| #use Pod::Simple::Debug (3); |
| use Pod::Simple::PullParser (); |
| |
| use Texinfo::Convert::NodeNameNormalization qw(normalize_node); |
| use Texinfo::Parser qw(parse_texi_line parse_texi_text); |
| use Texinfo::Convert::Texinfo; |
| use Texinfo::Convert::TextContent; |
| use Texinfo::Common qw(protect_comma_in_tree protect_first_parenthesis |
| protect_hashchar_at_line_beginning); |
| |
| use vars qw( |
| @ISA $VERSION |
| ); |
| |
| @ISA = ('Pod::Simple::PullParser'); |
| $VERSION = '0.01'; |
| |
| #use UNIVERSAL (); |
| |
| # Allows being called from the comand line as |
| # perl -w -MPod::Simple::Texinfo -e Pod::Simple::Texinfo::go thingy.pod |
| sub go { Pod::Simple::Texinfo->parse_from_file(@ARGV); exit 0 } |
| |
| my %head_commands_level; |
| foreach my $level (1 .. 4) { |
| $head_commands_level{'head'.$level} = $level; |
| } |
| |
| my @numbered_sectioning_commands = ('part', 'chapter', 'section', 'subsection', |
| 'subsubsection'); |
| my @appendix_sectioning_commands = ('part', 'appendix', 'appendixsec', |
| 'appendixsubsec', 'appendixsubsubsec'); |
| my @unnumbered_sectioning_commands = ('part', 'unnumbered', 'unnumberedsec', |
| 'unnumberedsubsec', 'unnumberedsubsubsec'); |
| |
| my @raw_formats = ('html', 'HTML', 'docbook', 'DocBook', 'texinfo', |
| 'Texinfo'); |
| |
| # from other Pod::Simple modules. Creates accessor subroutine. |
| __PACKAGE__->_accessorize( |
| 'texinfo_sectioning_base_level', |
| 'texinfo_short_title', |
| 'texinfo_man_url_prefix', |
| 'texinfo_sectioning_style', |
| 'texinfo_add_upper_sectioning_command', |
| 'texinfo_section_nodes', |
| 'texinfo_internal_pod_manuals', |
| ); |
| |
| my $sectioning_style = 'numbered'; |
| #my $sectioning_base_level = 2; |
| my $sectioning_base_level = 0; |
| my $man_url_prefix = 'http://man.he.net/man'; |
| |
| sub new |
| { |
| my $class = shift; |
| my $new = $class->SUPER::new(@_); |
| $new->accept_targets(@raw_formats); |
| $new->preserve_whitespace(1); |
| $new->texinfo_section_nodes(0); |
| $new->texinfo_sectioning_base_level ($sectioning_base_level); |
| $new->texinfo_man_url_prefix ($man_url_prefix); |
| $new->texinfo_sectioning_style ($sectioning_style); |
| $new->texinfo_add_upper_sectioning_command(1); |
| return $new; |
| } |
| |
| sub run |
| { |
| my $self = shift; |
| |
| # In case the caller changed the formats |
| my @formats = $self->accept_targets(); |
| foreach my $format (@formats) { |
| if (lc($format) eq 'texinfo') { |
| $self->{'texinfo_raw_format_commands'}->{$format} = ''; |
| $self->{'texinfo_if_format_commands'}->{':'.$format} = ''; |
| } else { |
| $self->{'texinfo_raw_format_commands'}->{$format} = lc($format); |
| $self->{'texinfo_if_format_commands'}->{':'.$format} = lc($format); |
| } |
| } |
| my $base_level = $self->texinfo_sectioning_base_level; |
| $base_level = 1 if ($base_level <= 1); |
| if ($self->texinfo_sectioning_style eq 'numbered') { |
| $self->{'texinfo_sectioning_commands'} = \@numbered_sectioning_commands; |
| } elsif ($self->texinfo_sectioning_style eq 'unnumbered') { |
| $self->{'texinfo_sectioning_commands'} = \@unnumbered_sectioning_commands; |
| } else { |
| $self->{'texinfo_sectioning_commands'} = \@appendix_sectioning_commands; |
| } |
| foreach my $heading_command (keys(%head_commands_level)) { |
| my $level = $head_commands_level{$heading_command} + $base_level -1; |
| if (!defined($self->{'texinfo_sectioning_commands'}->[$level])) { |
| $self->{'texinfo_head_commands'}->{$heading_command} |
| = $self->{'texinfo_sectioning_commands'}->[-1]; |
| } else { |
| $self->{'texinfo_head_commands'}->{$heading_command} |
| = $self->{'texinfo_sectioning_commands'}->[$level]; |
| } |
| } |
| $self->{'texinfo_internal_pod_manuals_hash'} = {}; |
| my $manuals = $self->texinfo_internal_pod_manuals(); |
| if ($manuals) { |
| foreach my $manual (@$manuals) { |
| $self->{'texinfo_internal_pod_manuals_hash'}->{$manual} = 1; |
| } |
| } |
| |
| if ($self->bare_output()) { |
| $self->_convert_pod(); |
| } else { |
| $self->_preamble(); |
| $self->_convert_pod(); |
| $self->_postamble(); |
| } |
| } |
| |
| my $STDIN_DOCU_NAME = 'stdin'; |
| sub _preamble($) |
| { |
| my $self = shift; |
| |
| my $fh = $self->{'output_fh'}; |
| |
| if (!defined($self->texinfo_short_title)) { |
| my $short_title = $self->get_short_title(); |
| if (defined($short_title) and $short_title =~ m/\S/) { |
| $self->texinfo_short_title($short_title); |
| } |
| } |
| |
| if ($self->texinfo_sectioning_base_level == 0) { |
| #print STDERR "$fh\n"; |
| print $fh '\input texinfo'."\n"; |
| my $setfilename; |
| if (defined($self->texinfo_short_title)) { |
| $setfilename = _pod_title_to_file_name($self->texinfo_short_title); |
| } else { |
| # FIXME maybe output filename would be better than source_filename? |
| my $source_filename = $self->source_filename(); |
| if (defined($source_filename) and $source_filename ne '') { |
| if ($source_filename eq '-') { |
| $setfilename = $STDIN_DOCU_NAME; |
| } else { |
| $setfilename = $source_filename; |
| $setfilename =~ s/\.(pod|pm)$//i; |
| } |
| } |
| } |
| if (defined($setfilename) and $setfilename =~ m/\S/) { |
| $setfilename = _protect_text($setfilename, 1); |
| $setfilename .= '.info'; |
| print $fh "\@setfilename $setfilename\n\n" |
| } |
| # FIXME depend on =encoding |
| print $fh '@documentencoding utf-8'."\n\n"; |
| |
| my $title = $self->get_title(); |
| if (defined($title) and $title =~ m/\S/) { |
| print $fh "\@settitle "._protect_text($title, 1)."\n\n"; |
| } |
| print $fh "\@node Top\n"; |
| if (defined($self->texinfo_short_title)) { |
| print $fh "\@top "._protect_text($self->texinfo_short_title, 1)."\n\n"; |
| } |
| } elsif (defined($self->texinfo_short_title) |
| and $self->texinfo_add_upper_sectioning_command) { |
| my $level = $self->texinfo_sectioning_base_level() - 1; |
| my $name = _protect_text($self->texinfo_short_title, 1); |
| my $node_name = _prepare_anchor($self, $name); |
| |
| my $anchor = ''; |
| my $node = ''; |
| if ($node_name =~ /\S/) { |
| if (!$self->texinfo_section_nodes |
| or $self->{'texinfo_sectioning_commands'}->[$level] eq 'part') { |
| $anchor = "\@anchor{$node_name}\n"; |
| } else { |
| $node = "\@node $node_name\n"; |
| } |
| } |
| print $fh "$node\@$self->{'texinfo_sectioning_commands'}->[$level] " |
| ._protect_text($self->texinfo_short_title, 1)."\n$anchor\n"; |
| } |
| } |
| |
| # 'out' is out of the context, for now for index entries. |
| sub _output($$$;$) |
| { |
| my $fh = shift; |
| my $accumulated_stack = shift; |
| my $text = shift; |
| my $out = shift; |
| |
| if (scalar(@$accumulated_stack)) { |
| if ($out) { |
| $accumulated_stack->[-1]->{'out'} .= $text; |
| } else { |
| $accumulated_stack->[-1]->{'text'} .= $text; |
| } |
| } else { |
| print $fh $text; |
| } |
| } |
| |
| sub _begin_context($$) |
| { |
| my $accumulated_stack = shift; |
| my $tag = shift; |
| push @$accumulated_stack, {'text' => '', 'tag' => $tag, |
| 'out' => ''}; |
| } |
| |
| sub _end_context($) |
| { |
| my $accumulated_stack = shift; |
| my $previous_context = pop @$accumulated_stack; |
| return ($previous_context->{'text'}, $previous_context->{'out'}); |
| } |
| |
| sub _protect_text($;$) |
| { |
| my $text = shift; |
| my $remove_new_lines = shift; |
| cluck if (!defined($text)); |
| $text =~ s/\n/ /g if ($remove_new_lines); |
| $text =~ s/([\@\{\}])/\@$1/g; |
| return $text; |
| } |
| |
| sub _pod_title_to_file_name($) |
| { |
| my $name = shift; |
| $name =~ s/\s+/_/g; |
| $name =~ s/::/-/g; |
| $name =~ s/[^\w\.-]//g; |
| $name = '_' if ($name eq ''); |
| return $name; |
| } |
| |
| sub _protect_comma($) |
| { |
| my $texinfo = shift; |
| my $tree = parse_texi_line(undef, $texinfo); |
| $tree = protect_comma_in_tree($tree); |
| return Texinfo::Convert::Texinfo::convert($tree); |
| } |
| |
| sub _protect_hashchar($) |
| { |
| my $texinfo = shift; |
| # protect # first in line |
| if ($texinfo =~ /#/) { |
| my $tree = parse_texi_text(undef, $texinfo); |
| protect_hashchar_at_line_beginning(undef, $tree); |
| return Texinfo::Convert::Texinfo::convert($tree); |
| } else { |
| return $texinfo; |
| } |
| } |
| |
| sub _reference_to_text_in_texi($) |
| { |
| my $texinfo = shift; |
| my $tree = parse_texi_text(undef, $texinfo); |
| Texinfo::Structuring::reference_to_arg_in_tree(undef, $tree); |
| return Texinfo::Convert::Texinfo::convert($tree); |
| } |
| |
| sub _section_manual_to_node_name($$$) |
| { |
| my $self = shift; |
| my $manual = shift; |
| my $section = shift; |
| my $base_level = shift; |
| |
| if (defined($manual) and $base_level > 0) { |
| return _protect_text($manual, 1). " $section"; |
| } else { |
| return $section; |
| } |
| } |
| |
| sub _normalize_texinfo_name($$) |
| { |
| # Pod may be more forgiven than Texinfo, so we go through |
| # a normalization, by parsing and converting back to Texinfo |
| my $name = shift; |
| my $command = shift; |
| my $texinfo_text; |
| if ($command eq 'anchor') { |
| $texinfo_text = "\@anchor{$name}"; |
| } else { |
| # item is not correct since it cannot happen outside of a table |
| # context, so we use @center which accepts the same on the line |
| if ($command eq 'item') { |
| $command = 'center'; |
| } |
| $texinfo_text = "\@$command $name\n"; |
| } |
| my $tree = parse_texi_text(undef, $texinfo_text); |
| if ($command eq 'anchor') { |
| #print STDERR "GGG $tree->{'contents'}->[0]->{'cmdname'}\n"; |
| $tree->{'contents'}->[0]->{'args'}->[-0]->{'contents'} |
| = protect_first_parenthesis($tree->{'contents'}->[0]->{'args'}->[-0]->{'contents'}); |
| } |
| my $fixed_text = Texinfo::Convert::Texinfo::convert($tree, 1); |
| my $result = $fixed_text; |
| if ($command eq 'anchor') { |
| $result =~ s/^\@anchor\{(.*)\}$/$1/s; |
| } else { |
| chomp($result); |
| $result =~ s/^\@$command (.*)$/$1/s; |
| } |
| return $result; |
| } |
| |
| sub _node_name($$) |
| { |
| my $self = shift; |
| my $texinfo_node_name = shift; |
| |
| chomp $texinfo_node_name; |
| $texinfo_node_name |
| = $self->_section_manual_to_node_name($self->texinfo_short_title, |
| $texinfo_node_name, |
| $self->texinfo_sectioning_base_level); |
| # also change refs to text |
| return _reference_to_text_in_texi($texinfo_node_name); |
| } |
| |
| sub _prepare_anchor($$) |
| { |
| my $self = shift; |
| my $texinfo_node_name = shift; |
| |
| my $node = _normalize_texinfo_name($texinfo_node_name, 'anchor'); |
| |
| if ($node !~ /\S/) { |
| return ''; |
| } |
| # Now we know that we have something. |
| my $node_tree = parse_texi_line(undef, $node); |
| my $normalized_base = normalize_node($node_tree); |
| my $normalized = $normalized_base; |
| my $number_appended = 0; |
| while ($self->{'texinfo_nodes'}->{$normalized}) { |
| $number_appended++; |
| $normalized = "${normalized_base}-$number_appended"; |
| } |
| my $node_name; |
| if ($number_appended) { |
| $texinfo_node_name = "$node $number_appended"; |
| $node_tree = parse_texi_line(undef, $texinfo_node_name); |
| } |
| $node_tree = protect_comma_in_tree($node_tree); |
| $self->{'texinfo_nodes'}->{$normalized} = $node_tree; |
| my $final_node_name = Texinfo::Convert::Texinfo::convert($node_tree, 1); |
| return $final_node_name; |
| } |
| |
| # from Pod::Simple::HTML general_url_escape |
| sub _url_escape($) |
| { |
| my $string = shift; |
| |
| $string =~ s/([^\x00-\xFF])/join '', map sprintf('%%%02X',$_), unpack 'C*', $1/eg; |
| # express Unicode things as urlencode(utf(orig)). |
| |
| # A pretty conservative escaping, behoovey even for query components |
| # of a URL (see RFC 2396) |
| |
| $string =~ s/([^-_\.!~*()abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789])/sprintf('%%%02X',ord($1))/eg; |
| # Yes, stipulate the list without a range, so that this can work right on |
| # all charsets that this module happens to run under. |
| # Altho, hmm, what about that ord? Presumably that won't work right |
| # under non-ASCII charsets. Something should be done |
| # about that, I guess? |
| |
| return $string; |
| } |
| |
| my %tag_commands = ( |
| 'F' => 'file', |
| 'S' => 'w', |
| 'I' => 'emph', |
| 'B' => 'strong', # or @b? |
| 'C' => 'code' |
| ); |
| |
| my %environment_commands = ( |
| 'Verbatim' => 'verbatim', |
| 'over-text' => 'table @asis', |
| 'over-bullet' => 'itemize', |
| 'over-number' => 'enumerate', |
| 'over-block' => 'quotation', |
| ); |
| |
| my %line_commands = ( |
| 'item-bullet' => 'item', |
| 'item-text' => 'item', |
| 'item-number' => 'item', |
| 'encoding' => 'documentencoding' |
| ); |
| |
| foreach my $tag (keys(%head_commands_level)) { |
| $line_commands{$tag} = 1; |
| } |
| |
| my %tags_index_before; |
| my %context_tags; |
| foreach my $context_tag (keys(%line_commands), 'L', 'X', 'Para') { |
| $context_tags{$context_tag} = 1; |
| } |
| |
| # do not appear as parsed token |
| # E entity/character |
| sub _convert_pod($) |
| { |
| my $self = shift; |
| |
| my $fh = $self->{'output_fh'}; |
| |
| my ($token, $type, $tagname, $top_seen); |
| |
| my @accumulated_output; |
| my @format_stack; |
| while($token = $self->get_token()) { |
| my $type = $token->type(); |
| #print STDERR "* type $type\n"; |
| #print STDERR $token->dump()."\n"; |
| if ($type eq 'start') { |
| my $tagname = $token->tagname(); |
| if ($context_tags{$tagname}) { |
| if ($tagname eq 'L') { |
| my $linktype = $token->attr('type'); |
| my $content_implicit = $token->attr('content-implicit'); |
| #print STDERR " L: $linktype\n"; |
| #my @attrs = keys %{$token->attr_hash}; |
| #print STDERR " @attrs\n"; |
| #my $raw_L = $token->attr('raw').''; |
| #print STDERR " $token->attr('raw'): $raw_L\n"; |
| my ($url_arg, $texinfo_node, $texinfo_manual, $texinfo_section); |
| if ($linktype eq 'man') { |
| # NOTE: the .'' is here to force the $token->attr to ba a real |
| # string and not an object. |
| # NOTE 2: It is not clear that setting the url should be done |
| # here, maybe this should be in the Texinfo HTML converter. |
| # However, there is a 'man' category here and not in Texinfo, |
| # so the information is more precise in pod. |
| my $replacement_arg = $token->attr('to').''; |
| # regexp from Pod::Simple::HTML resolve_man_page_link |
| # since it is very small, it is likely that copyright cannot be |
| # claimed for that part. |
| $replacement_arg =~ /^([^(]+)(?:[(](\d+)[)])?$/; |
| my $page = $1; |
| my $section = $2; |
| if (defined($page) and $page ne '') { |
| $section = 1 if (!defined($section)); |
| # it is unlikely that there is a comma because of _url_escape |
| # but to be sure there is still a call to _protect_comma. |
| $url_arg |
| = _protect_comma(_protect_text( |
| $self->texinfo_man_url_prefix |
| ."$section/"._url_escape($page))); |
| } else { |
| $url_arg = ''; |
| } |
| $replacement_arg = _protect_text($replacement_arg); |
| _output($fh, \@accumulated_output, "\@url{$url_arg,, $replacement_arg}"); |
| } elsif ($linktype eq 'url') { |
| # NOTE: the .'' is here to force the $token->attr to be a real |
| # string and not an object. |
| $url_arg = _protect_comma(_protect_text($token->attr('to').'')); |
| } elsif ($linktype eq 'pod') { |
| my $manual = $token->attr('to'); |
| my $section = $token->attr('section'); |
| $manual .= '' if (defined($manual)); |
| $section .= '' if (defined($section)); |
| if (0) { |
| my $section_text = 'UNDEF'; |
| if (defined($section)) { |
| $section_text = $section; |
| } |
| my $manual_text = 'UNDEF'; |
| if (defined($manual)) { |
| $manual_text = $manual; |
| } |
| print STDERR "L: $linktype $manual_text/$section_text\n"; |
| } |
| if (defined($manual)) { |
| if (! defined($section) or $section !~ m/\S/) { |
| if ($self->{'texinfo_internal_pod_manuals_hash'}->{$manual}) { |
| $section = 'NAME'; |
| } |
| } |
| if ($self->{'texinfo_internal_pod_manuals_hash'}->{$manual}) { |
| $texinfo_node = |
| $self->_section_manual_to_node_name($manual, $section, |
| $self->texinfo_sectioning_base_level); |
| } else { |
| $texinfo_manual = _protect_text(_pod_title_to_file_name($manual)); |
| if (defined($section)) { |
| $texinfo_node = $section; |
| } else { |
| $texinfo_node = ''; |
| } |
| } |
| } elsif (defined($section) and $section =~ m/\S/) { |
| $texinfo_node = |
| $self->_section_manual_to_node_name( |
| $self->texinfo_short_title, $section, |
| $self->texinfo_sectioning_base_level); |
| $texinfo_section = _normalize_texinfo_name( |
| _protect_comma(_protect_text($section)), 'section'); |
| #print STDERR "L: internal: $texinfo_node/$texinfo_section\n"; |
| } |
| $texinfo_node = _normalize_texinfo_name( |
| _protect_comma(_protect_text($texinfo_node)), 'anchor'); |
| #print STDERR "L: normalized node: $texinfo_node\n"; |
| |
| # for pod, 'to' is the pod manual name. Then 'section' is the |
| # section. |
| } |
| push @format_stack, [$linktype, $content_implicit, $url_arg, |
| $texinfo_manual, $texinfo_node, $texinfo_section]; |
| #if (defined($to)) { |
| # print STDERR " | $to\n"; |
| #} else { |
| # print STDERR "\n"; |
| #} |
| #print STDERR $token->dump."\n"; |
| } |
| _begin_context(\@accumulated_output, $tagname); |
| } elsif ($tag_commands{$tagname}) { |
| _output($fh, \@accumulated_output, "\@$tag_commands{$tagname}\{"); |
| } elsif ($environment_commands{$tagname}) { |
| _output($fh, \@accumulated_output, "\@$environment_commands{$tagname}\n"); |
| if ($tagname eq 'Verbatim') { |
| push @format_stack, 'verbatim'; |
| } |
| } elsif ($tagname eq 'for') { |
| my $target = $token->attr('target'); |
| push @format_stack, $target; |
| if ($self->{'texinfo_raw_format_commands'}->{$target}) { |
| _output($fh, \@accumulated_output, |
| "\@$self->{'texinfo_raw_format_commands'}->{$target}\n"); |
| } elsif ($self->{'texinfo_if_format_commands'}->{$target}) { |
| _output($fh, \@accumulated_output, |
| "\@if$self->{'texinfo_if_format_commands'}->{$target}\n"); |
| } |
| } |
| } elsif ($type eq 'text') { |
| my $text; |
| if (@format_stack and !ref($format_stack[-1]) |
| and ((defined($self->{'texinfo_raw_format_commands'}->{$format_stack[-1]}) |
| and !$self->{'texinfo_raw_format_commands'}->{$format_stack[-1]}) |
| or ($format_stack[-1] eq 'verbatim'))) { |
| $text = $token->text(); |
| } else { |
| $text = _protect_text($token->text()); |
| if (@format_stack and !ref($format_stack[-1]) |
| and ($self->{'texinfo_raw_format_commands'}->{$format_stack[-1]})) { |
| $text =~ s/^(\s*)#(\s*(line)? (\d+)(( "([^"]+)")(\s+\d+)*)?\s*)$/$1\@hashchar{}$2/mg; |
| } |
| } |
| _output($fh, \@accumulated_output, $text); |
| } elsif ($type eq 'end') { |
| my $tagname = $token->tagname(); |
| if ($context_tags{$tagname}) { |
| my ($result, $out) = _end_context(\@accumulated_output); |
| #print STDERR "end: $tagname: $result, $out\n"; |
| my $texinfo_node = ''; |
| if ($line_commands{$tagname}) { |
| |
| my ($command, $command_argument); |
| if ($head_commands_level{$tagname}) { |
| $command = $self->{'texinfo_head_commands'}->{$tagname}; |
| } elsif ($line_commands{$tagname}) { |
| $command = $line_commands{$tagname}; |
| } |
| |
| if ($head_commands_level{$tagname} or $tagname eq 'item-text') { |
| chomp ($result); |
| $result =~ s/\n/ /g; |
| $result =~ s/^\s*//; |
| $result =~ s/\s*$//; |
| |
| $command_argument = _normalize_texinfo_name($result, $command); |
| if ($result =~ /\S/ and $command_argument !~ /\S/) { |
| # use some raw text if the expansion lead to an empty section |
| my $tree = parse_texi_line(undef, $result); |
| my $converter = Texinfo::Convert::TextContent->converter(); |
| $command_argument = _protect_text($converter->convert_tree($tree)); |
| } |
| |
| my $anchor = ''; |
| my $node_name = _prepare_anchor($self, _node_name($self, $result)); |
| if ($node_name =~ /\S/) { |
| if ($tagname eq 'item-text' or !$self->texinfo_section_nodes) { |
| $anchor = "\n\@anchor{$node_name}"; |
| } else { |
| $texinfo_node = "\@node $node_name\n"; |
| } |
| } |
| $command_argument .= $anchor; |
| } else { |
| $command_argument = $result; |
| } |
| _output($fh, \@accumulated_output, |
| "$texinfo_node\@$command $command_argument\n$out\n"); |
| } elsif ($tagname eq 'Para') { |
| _output($fh, \@accumulated_output, $out. |
| _protect_hashchar($result)."\n\n"); |
| } elsif ($tagname eq 'L') { |
| my $format = pop @format_stack; |
| my ($linktype, $content_implicit, $url_arg, |
| $texinfo_manual, $texinfo_node, $texinfo_section) = @$format; |
| if ($linktype ne 'man') { |
| my $explanation; |
| if (defined($result) and $result =~ m/\S/ and !$content_implicit) { |
| $explanation = ' '. _protect_comma($result); |
| } |
| if ($linktype eq 'url') { |
| if (defined($explanation)) { |
| _output($fh, \@accumulated_output, |
| "\@url{$url_arg,$explanation}"); |
| } else { |
| _output($fh, \@accumulated_output, |
| "\@url{$url_arg}"); |
| } |
| } elsif ($linktype eq 'pod') { |
| if (defined($texinfo_manual)) { |
| $explanation = '' if (!defined($explanation)); |
| _output($fh, \@accumulated_output, |
| "\@ref{$texinfo_node,$explanation,, $texinfo_manual}"); |
| } elsif (defined($explanation)) { |
| _output($fh, \@accumulated_output, |
| "\@ref{$texinfo_node,$explanation,$explanation}"); |
| } else { |
| if (defined($texinfo_section) |
| and $texinfo_section ne $texinfo_node) { |
| _output($fh, \@accumulated_output, |
| "\@ref{$texinfo_node,, $texinfo_section}"); |
| } else { |
| _output($fh, \@accumulated_output, |
| "\@ref{$texinfo_node}"); |
| } |
| } |
| } |
| } |
| } elsif ($tagname eq 'X') { |
| my $next_token = $self->get_token(); |
| if ($next_token) { |
| if ($next_token->type() eq 'text') { |
| my $next_text = $next_token->text; |
| $next_text =~ s/^\s*//; |
| $next_token->text($next_text); |
| #_output($fh, \@accumulated_output, "\n"); |
| } |
| $self->unget_token($next_token); |
| } |
| chomp ($result); |
| $result =~ s/\n/ /g; |
| $result .= "\n"; |
| _output($fh, \@accumulated_output, "\@cindex $result", 1); |
| } |
| } elsif ($tag_commands{$tagname}) { |
| _output($fh, \@accumulated_output, "}"); |
| } elsif ($environment_commands{$tagname}) { |
| if ($tagname eq 'Verbatim') { |
| pop @format_stack; |
| _output($fh, \@accumulated_output, "\n"); |
| } |
| my $tag = $environment_commands{$tagname}; |
| $tag =~ s/ .*//; |
| _output($fh, \@accumulated_output, "\@end $tag\n\n"); |
| } elsif ($tagname eq 'for') { |
| my $target = pop @format_stack; |
| if ($self->{'texinfo_raw_format_commands'}->{$target}) { |
| _output($fh, \@accumulated_output, |
| "\n\@end $self->{'texinfo_raw_format_commands'}->{$target}\n"); |
| } elsif ($self->{'texinfo_if_format_commands'}->{$target}) { |
| _output($fh, \@accumulated_output, |
| "\@end if$self->{'texinfo_if_format_commands'}->{$target}\n"); |
| } |
| } |
| } |
| } |
| } |
| |
| sub _postamble($) |
| { |
| my $self = shift; |
| |
| my $fh = $self->{'output_fh'}; |
| if ($self->texinfo_sectioning_base_level == 0) { |
| #print STDERR "$fh\n"; |
| print $fh "\@bye\n"; |
| } |
| } |
| |
| 1; |
| |
| __END__ |
| |
| =head1 NAME |
| |
| Pod::Simple::Texinfo - format Pod as Texinfo |
| |
| =head1 SYNOPSIS |
| |
| # From the command like |
| perl -MPod::Simple::Texinfo -e Pod::Simple::Texinfo::go thingy.pod |
| |
| # From perl |
| my $new = Pod::Simple::Texinfo->new; |
| $new->texinfo_sectioning_style('unnumbered'); |
| my $from = shift @ARGV; |
| my $to = $from; |
| $to =~ s/\.(pod|pm)$/.texi/i; |
| $new->parse_from_file($from, $to); |
| |
| =head1 DESCRIPTION |
| |
| This class is for making a Texinfo rendering of a Pod document. |
| |
| This is a subclass of L<Pod::Simple::PullParser> and inherits all its |
| methods (and options). |
| |
| It supports producing a standalone manual per Pod (the default) or |
| render the Pod as a chapter, see L</texinfo_sectioning_base_level>. |
| |
| =head1 METHODS |
| |
| =over |
| |
| =item texinfo_sectioning_base_level |
| |
| Sets the level of the head1 commands. 1 is for the @chapter/@unnumbered |
| level. If set to 0, the head1 commands level is still 1, but the output |
| manual is considered to be a standalone manual. If not 0, the pod file is |
| rendered as a fragment of a Texinfo manual. |
| |
| =item texinfo_man_url_prefix |
| |
| String used as a prefix for man page urls. Default |
| is C<http://man.he.net/man>. |
| |
| =item texinfo_sectioning_style |
| |
| Default is C<numbered>, using the numbered sectioning Texinfo @-commands |
| (@chapter, @section...). Giving C<unnumbered> leads to using unnumbered |
| sectioning command variants (@unnumbered...), and any other value would |
| lead to using appendix sectioning command variants (@appendix...). |
| |
| =item texinfo_add_upper_sectioning_command |
| |
| If set (the default case), a sectioning command is added at the beginning |
| of the output for the whole document, using the module name, at the level |
| above the level set by L<texinfo_sectioning_base_level>. So there will be |
| a C<@part> if the level is equal to 1, a C<@chapter> if the level is equal |
| to 2 and so on and so forth. If the base level is 0, a C<@top> command is |
| output instead. |
| |
| =item texinfo_section_nodes |
| |
| If set, add C<@node> and not C<@anchor> for each sectioning command. |
| |
| =back |
| |
| =head1 SEE ALSO |
| |
| L<Pod::Simple>. L<Pod::Simple::PullParser>. The Texinfo manual. |
| |
| =head1 COPYRIGHT |
| |
| Copyright (C) 2011, 2012 Free Software Foundation, Inc. |
| |
| This library is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3 of the License, |
| or (at your option) any later version. |
| |
| C<_url_escape> is C<general_url_escape> from L<Pod::Simple::HTML>. |
| |
| =head1 AUTHOR |
| |
| Patrice Dumas E<lt>pertusus@free.frE<gt>. Parts from L<Pod::Simple::HTML>. |
| |
| =cut |