Texinfo.pm 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822
  1. # Texinfo.pm: format Pod as Texinfo.
  2. #
  3. # Copyright 2011, 2012, 2013, 2014 Free Software Foundation, Inc.
  4. #
  5. # This program is free software; you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation; either version 3 of the License,
  8. # or (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. #
  18. # Original author: Patrice Dumas <pertusus@free.fr>
  19. # Parts from L<Pod::Simple::HTML>.
  20. package Pod::Simple::Texinfo;
  21. require 5;
  22. use strict;
  23. use Carp qw(cluck);
  24. #use Pod::Simple::Debug (3);
  25. use Pod::Simple::PullParser ();
  26. use Texinfo::Convert::NodeNameNormalization qw(normalize_node);
  27. use Texinfo::Parser qw(parse_texi_line parse_texi_text);
  28. use Texinfo::Convert::Texinfo;
  29. use Texinfo::Convert::TextContent;
  30. use Texinfo::Common qw(protect_comma_in_tree protect_first_parenthesis
  31. protect_hashchar_at_line_beginning);
  32. use Texinfo::Transformations;
  33. use vars qw(
  34. @ISA $VERSION
  35. );
  36. @ISA = ('Pod::Simple::PullParser');
  37. $VERSION = '0.01';
  38. #use UNIVERSAL ();
  39. # Allows being called from the comand line as
  40. # perl -w -MPod::Simple::Texinfo -e Pod::Simple::Texinfo::go thingy.pod
  41. sub go { Pod::Simple::Texinfo->parse_from_file(@ARGV); exit 0 }
  42. my %head_commands_level;
  43. foreach my $level (1 .. 4) {
  44. $head_commands_level{'head'.$level} = $level;
  45. }
  46. my @numbered_sectioning_commands = ('part', 'chapter', 'section', 'subsection',
  47. 'subsubsection');
  48. my @appendix_sectioning_commands = ('part', 'appendix', 'appendixsec',
  49. 'appendixsubsec', 'appendixsubsubsec');
  50. my @unnumbered_sectioning_commands = ('part', 'unnumbered', 'unnumberedsec',
  51. 'unnumberedsubsec', 'unnumberedsubsubsec');
  52. my @raw_formats = ('html', 'HTML', 'docbook', 'DocBook', 'texinfo',
  53. 'Texinfo');
  54. # from other Pod::Simple modules. Creates accessor subroutine.
  55. __PACKAGE__->_accessorize(
  56. 'texinfo_sectioning_base_level',
  57. 'texinfo_short_title',
  58. 'texinfo_man_url_prefix',
  59. 'texinfo_sectioning_style',
  60. 'texinfo_add_upper_sectioning_command',
  61. 'texinfo_section_nodes',
  62. 'texinfo_internal_pod_manuals',
  63. );
  64. my $sectioning_style = 'numbered';
  65. #my $sectioning_base_level = 2;
  66. my $sectioning_base_level = 0;
  67. my $man_url_prefix = 'http://man.he.net/man';
  68. sub new
  69. {
  70. my $class = shift;
  71. my $new = $class->SUPER::new(@_);
  72. $new->accept_targets(@raw_formats);
  73. $new->preserve_whitespace(1);
  74. $new->texinfo_section_nodes(0);
  75. $new->texinfo_sectioning_base_level ($sectioning_base_level);
  76. $new->texinfo_man_url_prefix ($man_url_prefix);
  77. $new->texinfo_sectioning_style ($sectioning_style);
  78. $new->texinfo_add_upper_sectioning_command(1);
  79. return $new;
  80. }
  81. sub run
  82. {
  83. my $self = shift;
  84. # In case the caller changed the formats
  85. my @formats = $self->accept_targets();
  86. foreach my $format (@formats) {
  87. if (lc($format) eq 'texinfo') {
  88. $self->{'texinfo_raw_format_commands'}->{$format} = '';
  89. $self->{'texinfo_if_format_commands'}->{':'.$format} = '';
  90. } else {
  91. $self->{'texinfo_raw_format_commands'}->{$format} = lc($format);
  92. $self->{'texinfo_if_format_commands'}->{':'.$format} = lc($format);
  93. }
  94. }
  95. my $base_level = $self->texinfo_sectioning_base_level;
  96. $base_level = 1 if ($base_level <= 1);
  97. if ($self->texinfo_sectioning_style eq 'numbered') {
  98. $self->{'texinfo_sectioning_commands'} = \@numbered_sectioning_commands;
  99. } elsif ($self->texinfo_sectioning_style eq 'unnumbered') {
  100. $self->{'texinfo_sectioning_commands'} = \@unnumbered_sectioning_commands;
  101. } else {
  102. $self->{'texinfo_sectioning_commands'} = \@appendix_sectioning_commands;
  103. }
  104. foreach my $heading_command (keys(%head_commands_level)) {
  105. my $level = $head_commands_level{$heading_command} + $base_level -1;
  106. if (!defined($self->{'texinfo_sectioning_commands'}->[$level])) {
  107. $self->{'texinfo_head_commands'}->{$heading_command}
  108. = $self->{'texinfo_sectioning_commands'}->[-1];
  109. } else {
  110. $self->{'texinfo_head_commands'}->{$heading_command}
  111. = $self->{'texinfo_sectioning_commands'}->[$level];
  112. }
  113. }
  114. $self->{'texinfo_internal_pod_manuals_hash'} = {};
  115. my $manuals = $self->texinfo_internal_pod_manuals();
  116. if ($manuals) {
  117. foreach my $manual (@$manuals) {
  118. $self->{'texinfo_internal_pod_manuals_hash'}->{$manual} = 1;
  119. }
  120. }
  121. if ($self->bare_output()) {
  122. $self->_convert_pod();
  123. } else {
  124. $self->_preamble();
  125. $self->_convert_pod();
  126. $self->_postamble();
  127. }
  128. }
  129. my $STDIN_DOCU_NAME = 'stdin';
  130. sub _preamble($)
  131. {
  132. my $self = shift;
  133. my $fh = $self->{'output_fh'};
  134. if (!defined($self->texinfo_short_title)) {
  135. my $short_title = $self->get_short_title();
  136. if (defined($short_title) and $short_title =~ m/\S/) {
  137. $self->texinfo_short_title($short_title);
  138. }
  139. }
  140. if ($self->texinfo_sectioning_base_level == 0) {
  141. #print STDERR "$fh\n";
  142. print $fh '\input texinfo'."\n";
  143. my $setfilename;
  144. if (defined($self->texinfo_short_title)) {
  145. $setfilename = _pod_title_to_file_name($self->texinfo_short_title);
  146. } else {
  147. # FIXME maybe output filename would be better than source_filename?
  148. my $source_filename = $self->source_filename();
  149. if (defined($source_filename) and $source_filename ne '') {
  150. if ($source_filename eq '-') {
  151. $setfilename = $STDIN_DOCU_NAME;
  152. } else {
  153. $setfilename = $source_filename;
  154. $setfilename =~ s/\.(pod|pm)$//i;
  155. }
  156. }
  157. }
  158. if (defined($setfilename) and $setfilename =~ m/\S/) {
  159. $setfilename = _protect_text($setfilename, 1);
  160. $setfilename .= '.info';
  161. print $fh "\@setfilename $setfilename\n\n"
  162. }
  163. # FIXME depend on =encoding
  164. print $fh '@documentencoding utf-8'."\n\n";
  165. my $title = $self->get_title();
  166. if (defined($title) and $title =~ m/\S/) {
  167. print $fh "\@settitle "._protect_text($title, 1)."\n\n";
  168. }
  169. print $fh "\@node Top\n";
  170. if (defined($self->texinfo_short_title)) {
  171. print $fh "\@top "._protect_text($self->texinfo_short_title, 1)."\n\n";
  172. }
  173. } elsif (defined($self->texinfo_short_title)
  174. and $self->texinfo_add_upper_sectioning_command) {
  175. my $level = $self->texinfo_sectioning_base_level() - 1;
  176. my $name = _protect_text($self->texinfo_short_title, 1);
  177. my $node_name = _prepare_anchor($self, $name);
  178. my $anchor = '';
  179. my $node = '';
  180. if ($node_name =~ /\S/) {
  181. if (!$self->texinfo_section_nodes
  182. or $self->{'texinfo_sectioning_commands'}->[$level] eq 'part') {
  183. $anchor = "\@anchor{$node_name}\n";
  184. } else {
  185. $node = "\@node $node_name\n";
  186. }
  187. }
  188. print $fh "$node\@$self->{'texinfo_sectioning_commands'}->[$level] "
  189. ._protect_text($self->texinfo_short_title, 1)."\n$anchor\n";
  190. }
  191. }
  192. # 'out' is out of the context, for now for index entries.
  193. sub _output($$$;$)
  194. {
  195. my $fh = shift;
  196. my $accumulated_stack = shift;
  197. my $text = shift;
  198. my $out = shift;
  199. if (scalar(@$accumulated_stack)) {
  200. if ($out) {
  201. $accumulated_stack->[-1]->{'out'} .= $text;
  202. } else {
  203. $accumulated_stack->[-1]->{'text'} .= $text;
  204. }
  205. } else {
  206. print $fh $text;
  207. }
  208. }
  209. sub _begin_context($$)
  210. {
  211. my $accumulated_stack = shift;
  212. my $tag = shift;
  213. push @$accumulated_stack, {'text' => '', 'tag' => $tag,
  214. 'out' => ''};
  215. }
  216. sub _end_context($)
  217. {
  218. my $accumulated_stack = shift;
  219. my $previous_context = pop @$accumulated_stack;
  220. return ($previous_context->{'text'}, $previous_context->{'out'});
  221. }
  222. sub _protect_text($;$)
  223. {
  224. my $text = shift;
  225. my $remove_new_lines = shift;
  226. cluck if (!defined($text));
  227. $text =~ s/\n/ /g if ($remove_new_lines);
  228. $text =~ s/([\@\{\}])/\@$1/g;
  229. return $text;
  230. }
  231. sub _pod_title_to_file_name($)
  232. {
  233. my $name = shift;
  234. $name =~ s/\s+/_/g;
  235. $name =~ s/::/-/g;
  236. $name =~ s/[^\w\.-]//g;
  237. $name = '_' if ($name eq '');
  238. return $name;
  239. }
  240. sub _protect_comma($)
  241. {
  242. my $texinfo = shift;
  243. my $tree = parse_texi_line(undef, $texinfo);
  244. $tree = protect_comma_in_tree($tree);
  245. return Texinfo::Convert::Texinfo::convert($tree);
  246. }
  247. sub _protect_hashchar($)
  248. {
  249. my $texinfo = shift;
  250. # protect # first in line
  251. if ($texinfo =~ /#/) {
  252. my $tree = parse_texi_text(undef, $texinfo);
  253. protect_hashchar_at_line_beginning(undef, $tree);
  254. return Texinfo::Convert::Texinfo::convert($tree);
  255. } else {
  256. return $texinfo;
  257. }
  258. }
  259. sub _reference_to_text_in_texi($)
  260. {
  261. my $texinfo = shift;
  262. my $tree = parse_texi_text(undef, $texinfo);
  263. Texinfo::Transformations::reference_to_arg_in_tree(undef, $tree);
  264. return Texinfo::Convert::Texinfo::convert($tree);
  265. }
  266. sub _section_manual_to_node_name($$$)
  267. {
  268. my $self = shift;
  269. my $manual = shift;
  270. my $section = shift;
  271. my $base_level = shift;
  272. if (defined($manual) and $base_level > 0) {
  273. return _protect_text($manual, 1). " $section";
  274. } else {
  275. return $section;
  276. }
  277. }
  278. sub _normalize_texinfo_name($$)
  279. {
  280. # Pod may be more forgiven than Texinfo, so we go through
  281. # a normalization, by parsing and converting back to Texinfo
  282. my $name = shift;
  283. my $command = shift;
  284. my $texinfo_text;
  285. if ($command eq 'anchor') {
  286. $texinfo_text = "\@anchor{$name}";
  287. } else {
  288. # item is not correct since it cannot happen outside of a table
  289. # context, so we use @center which accepts the same on the line
  290. if ($command eq 'item') {
  291. $command = 'center';
  292. }
  293. $texinfo_text = "\@$command $name\n";
  294. }
  295. my $tree = parse_texi_text(undef, $texinfo_text);
  296. if ($command eq 'anchor') {
  297. #print STDERR "GGG $tree->{'contents'}->[0]->{'cmdname'}\n";
  298. $tree->{'contents'}->[0]->{'args'}->[-0]->{'contents'}
  299. = protect_first_parenthesis($tree->{'contents'}->[0]->{'args'}->[-0]->{'contents'});
  300. }
  301. my $fixed_text = Texinfo::Convert::Texinfo::convert($tree, 1);
  302. my $result = $fixed_text;
  303. if ($command eq 'anchor') {
  304. $result =~ s/^\@anchor\{(.*)\}$/$1/s;
  305. } else {
  306. chomp($result);
  307. $result =~ s/^\@$command (.*)$/$1/s;
  308. }
  309. return $result;
  310. }
  311. sub _node_name($$)
  312. {
  313. my $self = shift;
  314. my $texinfo_node_name = shift;
  315. chomp $texinfo_node_name;
  316. $texinfo_node_name
  317. = $self->_section_manual_to_node_name($self->texinfo_short_title,
  318. $texinfo_node_name,
  319. $self->texinfo_sectioning_base_level);
  320. # also change refs to text
  321. return _reference_to_text_in_texi($texinfo_node_name);
  322. }
  323. sub _prepare_anchor($$)
  324. {
  325. my $self = shift;
  326. my $texinfo_node_name = shift;
  327. my $node = _normalize_texinfo_name($texinfo_node_name, 'anchor');
  328. if ($node !~ /\S/) {
  329. return '';
  330. }
  331. # Now we know that we have something.
  332. my $node_tree = parse_texi_line(undef, $node);
  333. my $normalized_base = normalize_node($node_tree);
  334. my $normalized = $normalized_base;
  335. my $number_appended = 0;
  336. while ($self->{'texinfo_nodes'}->{$normalized}) {
  337. $number_appended++;
  338. $normalized = "${normalized_base}-$number_appended";
  339. }
  340. my $node_name;
  341. if ($number_appended) {
  342. $texinfo_node_name = "$node $number_appended";
  343. $node_tree = parse_texi_line(undef, $texinfo_node_name);
  344. }
  345. $node_tree = protect_comma_in_tree($node_tree);
  346. $self->{'texinfo_nodes'}->{$normalized} = $node_tree;
  347. my $final_node_name = Texinfo::Convert::Texinfo::convert($node_tree, 1);
  348. return $final_node_name;
  349. }
  350. # from Pod::Simple::HTML general_url_escape
  351. sub _url_escape($)
  352. {
  353. my $string = shift;
  354. $string =~ s/([^\x00-\xFF])/join '', map sprintf('%%%02X',$_), unpack 'C*', $1/eg;
  355. # express Unicode things as urlencode(utf(orig)).
  356. # A pretty conservative escaping, behoovey even for query components
  357. # of a URL (see RFC 2396)
  358. $string =~ s/([^-_\.!~*()abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789])/sprintf('%%%02X',ord($1))/eg;
  359. # Yes, stipulate the list without a range, so that this can work right on
  360. # all charsets that this module happens to run under.
  361. # Altho, hmm, what about that ord? Presumably that won't work right
  362. # under non-ASCII charsets. Something should be done
  363. # about that, I guess?
  364. return $string;
  365. }
  366. my %tag_commands = (
  367. 'F' => 'file',
  368. 'S' => 'w',
  369. 'I' => 'emph',
  370. 'B' => 'strong', # or @b?
  371. 'C' => 'code'
  372. );
  373. my %environment_commands = (
  374. 'Verbatim' => 'verbatim',
  375. 'over-text' => 'table @asis',
  376. 'over-bullet' => 'itemize',
  377. 'over-number' => 'enumerate',
  378. 'over-block' => 'quotation',
  379. );
  380. my %line_commands = (
  381. 'item-bullet' => 'item',
  382. 'item-text' => 'item',
  383. 'item-number' => 'item',
  384. 'encoding' => 'documentencoding'
  385. );
  386. foreach my $tag (keys(%head_commands_level)) {
  387. $line_commands{$tag} = 1;
  388. }
  389. my %tags_index_before;
  390. my %context_tags;
  391. foreach my $context_tag (keys(%line_commands), 'L', 'X', 'Para') {
  392. $context_tags{$context_tag} = 1;
  393. }
  394. # do not appear as parsed token
  395. # E entity/character
  396. sub _convert_pod($)
  397. {
  398. my $self = shift;
  399. my $fh = $self->{'output_fh'};
  400. my ($token, $type, $tagname, $top_seen);
  401. my @accumulated_output;
  402. my @format_stack;
  403. while($token = $self->get_token()) {
  404. my $type = $token->type();
  405. #print STDERR "* type $type\n";
  406. #print STDERR $token->dump()."\n";
  407. if ($type eq 'start') {
  408. my $tagname = $token->tagname();
  409. if ($context_tags{$tagname}) {
  410. if ($tagname eq 'L') {
  411. my $linktype = $token->attr('type');
  412. my $content_implicit = $token->attr('content-implicit');
  413. #print STDERR " L: $linktype\n";
  414. #my @attrs = keys %{$token->attr_hash};
  415. #print STDERR " @attrs\n";
  416. #my $raw_L = $token->attr('raw').'';
  417. #print STDERR " $token->attr('raw'): $raw_L\n";
  418. my ($url_arg, $texinfo_node, $texinfo_manual, $texinfo_section);
  419. if ($linktype eq 'man') {
  420. # NOTE: the .'' is here to force the $token->attr to ba a real
  421. # string and not an object.
  422. # NOTE 2: It is not clear that setting the url should be done
  423. # here, maybe this should be in the Texinfo HTML converter.
  424. # However, there is a 'man' category here and not in Texinfo,
  425. # so the information is more precise in pod.
  426. my $replacement_arg = $token->attr('to').'';
  427. # regexp from Pod::Simple::HTML resolve_man_page_link
  428. # since it is very small, it is likely that copyright cannot be
  429. # claimed for that part.
  430. $replacement_arg =~ /^([^(]+)(?:[(](\d+)[)])?$/;
  431. my $page = $1;
  432. my $section = $2;
  433. if (defined($page) and $page ne '') {
  434. $section = 1 if (!defined($section));
  435. # it is unlikely that there is a comma because of _url_escape
  436. # but to be sure there is still a call to _protect_comma.
  437. $url_arg
  438. = _protect_comma(_protect_text(
  439. $self->texinfo_man_url_prefix
  440. ."$section/"._url_escape($page)));
  441. } else {
  442. $url_arg = '';
  443. }
  444. $replacement_arg = _protect_text($replacement_arg);
  445. _output($fh, \@accumulated_output, "\@url{$url_arg,, $replacement_arg}");
  446. } elsif ($linktype eq 'url') {
  447. # NOTE: the .'' is here to force the $token->attr to be a real
  448. # string and not an object.
  449. $url_arg = _protect_comma(_protect_text($token->attr('to').''));
  450. } elsif ($linktype eq 'pod') {
  451. my $manual = $token->attr('to');
  452. my $section = $token->attr('section');
  453. $manual .= '' if (defined($manual));
  454. $section .= '' if (defined($section));
  455. if (0) {
  456. my $section_text = 'UNDEF';
  457. if (defined($section)) {
  458. $section_text = $section;
  459. }
  460. my $manual_text = 'UNDEF';
  461. if (defined($manual)) {
  462. $manual_text = $manual;
  463. }
  464. print STDERR "L: $linktype $manual_text/$section_text\n";
  465. }
  466. if (defined($manual)) {
  467. if (! defined($section) or $section !~ m/\S/) {
  468. if ($self->{'texinfo_internal_pod_manuals_hash'}->{$manual}) {
  469. $section = 'NAME';
  470. }
  471. }
  472. if ($self->{'texinfo_internal_pod_manuals_hash'}->{$manual}) {
  473. $texinfo_node =
  474. $self->_section_manual_to_node_name($manual, $section,
  475. $self->texinfo_sectioning_base_level);
  476. } else {
  477. $texinfo_manual = _protect_text(_pod_title_to_file_name($manual));
  478. if (defined($section)) {
  479. $texinfo_node = $section;
  480. } else {
  481. $texinfo_node = '';
  482. }
  483. }
  484. } elsif (defined($section) and $section =~ m/\S/) {
  485. $texinfo_node =
  486. $self->_section_manual_to_node_name(
  487. $self->texinfo_short_title, $section,
  488. $self->texinfo_sectioning_base_level);
  489. $texinfo_section = _normalize_texinfo_name(
  490. _protect_comma(_protect_text($section)), 'section');
  491. #print STDERR "L: internal: $texinfo_node/$texinfo_section\n";
  492. }
  493. $texinfo_node = _normalize_texinfo_name(
  494. _protect_comma(_protect_text($texinfo_node)), 'anchor');
  495. #print STDERR "L: normalized node: $texinfo_node\n";
  496. # for pod, 'to' is the pod manual name. Then 'section' is the
  497. # section.
  498. }
  499. push @format_stack, [$linktype, $content_implicit, $url_arg,
  500. $texinfo_manual, $texinfo_node, $texinfo_section];
  501. #if (defined($to)) {
  502. # print STDERR " | $to\n";
  503. #} else {
  504. # print STDERR "\n";
  505. #}
  506. #print STDERR $token->dump."\n";
  507. }
  508. _begin_context(\@accumulated_output, $tagname);
  509. } elsif ($tag_commands{$tagname}) {
  510. _output($fh, \@accumulated_output, "\@$tag_commands{$tagname}\{");
  511. } elsif ($environment_commands{$tagname}) {
  512. _output($fh, \@accumulated_output, "\@$environment_commands{$tagname}\n");
  513. if ($tagname eq 'Verbatim') {
  514. push @format_stack, 'verbatim';
  515. }
  516. } elsif ($tagname eq 'for') {
  517. my $target = $token->attr('target');
  518. push @format_stack, $target;
  519. if ($self->{'texinfo_raw_format_commands'}->{$target}) {
  520. _output($fh, \@accumulated_output,
  521. "\@$self->{'texinfo_raw_format_commands'}->{$target}\n");
  522. } elsif ($self->{'texinfo_if_format_commands'}->{$target}) {
  523. _output($fh, \@accumulated_output,
  524. "\@if$self->{'texinfo_if_format_commands'}->{$target}\n");
  525. }
  526. }
  527. } elsif ($type eq 'text') {
  528. my $text;
  529. if (@format_stack and !ref($format_stack[-1])
  530. and ((defined($self->{'texinfo_raw_format_commands'}->{$format_stack[-1]})
  531. and !$self->{'texinfo_raw_format_commands'}->{$format_stack[-1]})
  532. or ($format_stack[-1] eq 'verbatim'))) {
  533. $text = $token->text();
  534. } else {
  535. $text = _protect_text($token->text());
  536. if (@format_stack and !ref($format_stack[-1])
  537. and ($self->{'texinfo_raw_format_commands'}->{$format_stack[-1]})) {
  538. $text =~ s/^(\s*)#(\s*(line)? (\d+)(( "([^"]+)")(\s+\d+)*)?\s*)$/$1\@hashchar{}$2/mg;
  539. }
  540. }
  541. _output($fh, \@accumulated_output, $text);
  542. } elsif ($type eq 'end') {
  543. my $tagname = $token->tagname();
  544. if ($context_tags{$tagname}) {
  545. my ($result, $out) = _end_context(\@accumulated_output);
  546. #print STDERR "end: $tagname: $result, $out\n";
  547. my $texinfo_node = '';
  548. if ($line_commands{$tagname}) {
  549. my ($command, $command_argument);
  550. if ($head_commands_level{$tagname}) {
  551. $command = $self->{'texinfo_head_commands'}->{$tagname};
  552. } elsif ($line_commands{$tagname}) {
  553. $command = $line_commands{$tagname};
  554. }
  555. if ($head_commands_level{$tagname} or $tagname eq 'item-text') {
  556. chomp ($result);
  557. $result =~ s/\n/ /g;
  558. $result =~ s/^\s*//;
  559. $result =~ s/\s*$//;
  560. $command_argument = _normalize_texinfo_name($result, $command);
  561. if ($result =~ /\S/ and $command_argument !~ /\S/) {
  562. # use some raw text if the expansion lead to an empty section
  563. my $tree = parse_texi_line(undef, $result);
  564. my $converter = Texinfo::Convert::TextContent->converter();
  565. $command_argument = _protect_text($converter->convert_tree($tree));
  566. }
  567. my $anchor = '';
  568. my $node_name = _prepare_anchor($self, _node_name($self, $result));
  569. if ($node_name =~ /\S/) {
  570. if ($tagname eq 'item-text' or !$self->texinfo_section_nodes) {
  571. $anchor = "\n\@anchor{$node_name}";
  572. } else {
  573. $texinfo_node = "\@node $node_name\n";
  574. }
  575. }
  576. $command_argument .= $anchor;
  577. } else {
  578. $command_argument = $result;
  579. }
  580. _output($fh, \@accumulated_output,
  581. "$texinfo_node\@$command $command_argument\n$out\n");
  582. } elsif ($tagname eq 'Para') {
  583. _output($fh, \@accumulated_output, $out.
  584. _protect_hashchar($result)."\n\n");
  585. } elsif ($tagname eq 'L') {
  586. my $format = pop @format_stack;
  587. my ($linktype, $content_implicit, $url_arg,
  588. $texinfo_manual, $texinfo_node, $texinfo_section) = @$format;
  589. if ($linktype ne 'man') {
  590. my $explanation;
  591. if (defined($result) and $result =~ m/\S/ and !$content_implicit) {
  592. $explanation = ' '. _protect_comma($result);
  593. }
  594. if ($linktype eq 'url') {
  595. if (defined($explanation)) {
  596. _output($fh, \@accumulated_output,
  597. "\@url{$url_arg,$explanation}");
  598. } else {
  599. _output($fh, \@accumulated_output,
  600. "\@url{$url_arg}");
  601. }
  602. } elsif ($linktype eq 'pod') {
  603. if (defined($texinfo_manual)) {
  604. $explanation = '' if (!defined($explanation));
  605. _output($fh, \@accumulated_output,
  606. "\@ref{$texinfo_node,$explanation,, $texinfo_manual}");
  607. } elsif (defined($explanation)) {
  608. _output($fh, \@accumulated_output,
  609. "\@ref{$texinfo_node,$explanation,$explanation}");
  610. } else {
  611. if (defined($texinfo_section)
  612. and $texinfo_section ne $texinfo_node) {
  613. _output($fh, \@accumulated_output,
  614. "\@ref{$texinfo_node,, $texinfo_section}");
  615. } else {
  616. _output($fh, \@accumulated_output,
  617. "\@ref{$texinfo_node}");
  618. }
  619. }
  620. }
  621. }
  622. } elsif ($tagname eq 'X') {
  623. my $next_token = $self->get_token();
  624. if ($next_token) {
  625. if ($next_token->type() eq 'text') {
  626. my $next_text = $next_token->text;
  627. $next_text =~ s/^\s*//;
  628. $next_token->text($next_text);
  629. #_output($fh, \@accumulated_output, "\n");
  630. }
  631. $self->unget_token($next_token);
  632. }
  633. chomp ($result);
  634. $result =~ s/\n/ /g;
  635. $result .= "\n";
  636. _output($fh, \@accumulated_output, "\@cindex $result", 1);
  637. }
  638. } elsif ($tag_commands{$tagname}) {
  639. _output($fh, \@accumulated_output, "}");
  640. } elsif ($environment_commands{$tagname}) {
  641. if ($tagname eq 'Verbatim') {
  642. pop @format_stack;
  643. _output($fh, \@accumulated_output, "\n");
  644. }
  645. my $tag = $environment_commands{$tagname};
  646. $tag =~ s/ .*//;
  647. _output($fh, \@accumulated_output, "\@end $tag\n\n");
  648. } elsif ($tagname eq 'for') {
  649. my $target = pop @format_stack;
  650. if ($self->{'texinfo_raw_format_commands'}->{$target}) {
  651. _output($fh, \@accumulated_output,
  652. "\n\@end $self->{'texinfo_raw_format_commands'}->{$target}\n");
  653. } elsif ($self->{'texinfo_if_format_commands'}->{$target}) {
  654. _output($fh, \@accumulated_output,
  655. "\@end if$self->{'texinfo_if_format_commands'}->{$target}\n");
  656. }
  657. }
  658. }
  659. }
  660. }
  661. sub _postamble($)
  662. {
  663. my $self = shift;
  664. my $fh = $self->{'output_fh'};
  665. if ($self->texinfo_sectioning_base_level == 0) {
  666. #print STDERR "$fh\n";
  667. print $fh "\@bye\n";
  668. }
  669. }
  670. 1;
  671. __END__
  672. =head1 NAME
  673. Pod::Simple::Texinfo - format Pod as Texinfo
  674. =head1 SYNOPSIS
  675. # From the command like
  676. perl -MPod::Simple::Texinfo -e Pod::Simple::Texinfo::go thingy.pod
  677. # From perl
  678. my $new = Pod::Simple::Texinfo->new;
  679. $new->texinfo_sectioning_style('unnumbered');
  680. my $from = shift @ARGV;
  681. my $to = $from;
  682. $to =~ s/\.(pod|pm)$/.texi/i;
  683. $new->parse_from_file($from, $to);
  684. =head1 DESCRIPTION
  685. This class is for making a Texinfo rendering of a Pod document.
  686. This is a subclass of L<Pod::Simple::PullParser> and inherits all its
  687. methods (and options).
  688. It supports producing a standalone manual per Pod (the default) or
  689. render the Pod as a chapter, see L</texinfo_sectioning_base_level>.
  690. =head1 METHODS
  691. =over
  692. =item texinfo_sectioning_base_level
  693. Sets the level of the head1 commands. 1 is for the @chapter/@unnumbered
  694. level. If set to 0, the head1 commands level is still 1, but the output
  695. manual is considered to be a standalone manual. If not 0, the pod file is
  696. rendered as a fragment of a Texinfo manual.
  697. =item texinfo_man_url_prefix
  698. String used as a prefix for man page urls. Default
  699. is C<http://man.he.net/man>.
  700. =item texinfo_sectioning_style
  701. Default is C<numbered>, using the numbered sectioning Texinfo @-commands
  702. (@chapter, @section...). Giving C<unnumbered> leads to using unnumbered
  703. sectioning command variants (@unnumbered...), and any other value would
  704. lead to using appendix sectioning command variants (@appendix...).
  705. =item texinfo_add_upper_sectioning_command
  706. If set (the default case), a sectioning command is added at the beginning
  707. of the output for the whole document, using the module name, at the level
  708. above the level set by L<texinfo_sectioning_base_level>. So there will be
  709. a C<@part> if the level is equal to 1, a C<@chapter> if the level is equal
  710. to 2 and so on and so forth. If the base level is 0, a C<@top> command is
  711. output instead.
  712. =item texinfo_section_nodes
  713. If set, add C<@node> and not C<@anchor> for each sectioning command.
  714. =back
  715. =head1 SEE ALSO
  716. L<Pod::Simple>. L<Pod::Simple::PullParser>. The Texinfo manual.
  717. =head1 COPYRIGHT
  718. Copyright (C) 2011, 2012 Free Software Foundation, Inc.
  719. This library is free software; you can redistribute it and/or modify
  720. it under the terms of the GNU General Public License as published by
  721. the Free Software Foundation; either version 3 of the License,
  722. or (at your option) any later version.
  723. C<_url_escape> is C<general_url_escape> from L<Pod::Simple::HTML>.
  724. =head1 AUTHOR
  725. Patrice Dumas E<lt>pertusus@free.frE<gt>. Parts from L<Pod::Simple::HTML>.
  726. =cut