1#! /usr/bin/env perl
2
3# texi2any: Texinfo converter.
4#
5# Copyright 2010-2021 Free Software Foundation, Inc.
6#
7# This program is free software; you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation; either version 3 of the License,
10# or (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with this program.  If not, see <http://www.gnu.org/licenses/>.
19#
20# Original author: Patrice Dumas <pertusus@free.fr>
21# Parts (also from Patrice Dumas) come from texi2html.pl or texi2html.init.
22
23# for POSIX::setlocale and File::Spec
24require 5.00405;
25
26use strict;
27
28# for file names portability
29use File::Spec;
30# to determine the path separator and null file
31use Config;
32# for dirname and fileparse
33use File::Basename;
34#use Cwd;
35use Getopt::Long qw(GetOptions);
36# for carp
37#use Carp;
38
39Getopt::Long::Configure("gnu_getopt");
40
41my ($real_command_name, $command_directory, $command_suffix);
42
43# This big BEGIN block deals with finding modules and
44# some dependencies that we ship
45# * in source or
46# * installed or
47# * installed relative to the script
48BEGIN
49{
50  # emulate -w
51  $^W = 1;
52  ($real_command_name, $command_directory, $command_suffix)
53     = fileparse($0, '.pl');
54  my $updir = File::Spec->updir();
55
56  # These are substituted by the Makefile to create "texi2any".
57  my $datadir = '@datadir@';
58  my $package = '@PACKAGE@';
59  my $packagedir = '@pkglibdir@';
60
61  if ($datadir eq '@' .'datadir@'
62      or defined($ENV{'TEXINFO_DEV_SOURCE'})
63         and $ENV{'TEXINFO_DEV_SOURCE'} ne '0')
64  {
65    # Use uninstalled modules
66
67    # To find Texinfo::ModulePath
68    if (defined($ENV{'top_builddir'})) {
69      unshift @INC, File::Spec->catdir($ENV{'top_builddir'}, 'tp');
70    } else {
71      unshift @INC, File::Spec->catdir($command_directory);
72    }
73
74    require Texinfo::ModulePath;
75    Texinfo::ModulePath::init(undef, undef, 'updirs' => 1);
76  } else {
77    # Look for modules in their installed locations.
78
79    my $lib_dir = File::Spec->catdir($datadir, $package);
80    unshift @INC, $lib_dir;
81
82    require Texinfo::ModulePath;
83    Texinfo::ModulePath::init($lib_dir, $packagedir, 'installed' => 1);
84  }
85} # end BEGIN
86
87# This allows disabling use of XS modules when Texinfo is built.
88BEGIN {
89  my $enable_xs = '@enable_xs@';
90  if ($enable_xs eq 'no') {
91    package Texinfo::XSLoader;
92    our $disable_XS;
93    $disable_XS = 1;
94  }
95}
96
97use Locale::Messages;
98use Texinfo::Common;
99use Texinfo::Convert::Converter;
100
101# this associates the command line options to the arrays set during
102# command line parsing.
103my @css_files = ();
104my @css_refs = ();
105my $cmdline_options = { 'CSS_FILES' => \@css_files,
106                        'CSS_REFS' => \@css_refs };
107
108# determine the path separators
109my $path_separator = $Config{'path_sep'};
110$path_separator = ':' if (!defined($path_separator));
111my $quoted_path_separator = quotemeta($path_separator);
112
113# Paths and file names
114my $curdir = File::Spec->curdir();
115my $updir = File::Spec->updir();
116
117# set by configure, prefix for the sysconfdir and so on
118# This could be used in the eval
119my $prefix = '@prefix@';
120my $datarootdir;
121my $sysconfdir;
122my $pkgdatadir;
123my $datadir;
124
125my $fallback_prefix = File::Spec->catdir(File::Spec->rootdir(), 'usr', 'local');
126
127# We need to eval as $prefix has to be expanded. However when we haven't
128# run configure @sysconfdir will be expanded as an array, thus we verify
129# whether configure was run or not
130if ('@sysconfdir@' ne '@' . 'sysconfdir@') {
131  $sysconfdir = eval '"@sysconfdir@"';
132} else {
133  $sysconfdir = File::Spec->catdir($fallback_prefix, 'etc');
134}
135
136if ('@datarootdir@' ne '@' . 'datarootdir@') {
137  $datarootdir = eval '"@datarootdir@"';
138} else {
139  $datarootdir = File::Spec->catdir($fallback_prefix, 'share');
140}
141
142if ('@datadir@' ne '@' . 'datadir@' and '@PACKAGE@' ne '@' . 'PACKAGE@') {
143  $datadir = eval '"@datadir@"';
144  my $package = '@PACKAGE@';
145  $pkgdatadir = File::Spec->catdir($datadir, $package);
146} else {
147  $datadir = File::Spec->catdir($fallback_prefix, 'share');
148  $pkgdatadir = File::Spec->catdir($datadir, 'texinfo');
149}
150
151# work-around in case libintl-perl do not do it itself
152# see http://www.gnu.org/software/gettext/manual/html_node/The-LANGUAGE-variable.html#The-LANGUAGE-variable
153
154if ((defined($ENV{"LC_ALL"}) and $ENV{"LC_ALL"} =~ /^(C|POSIX)$/)
155     or (defined($ENV{"LANG"}) and $ENV{"LANG"} =~ /^(C|POSIX)$/)) {
156  delete $ENV{"LANGUAGE"} if defined($ENV{"LANGUAGE"});
157}
158
159
160#my $messages_textdomain = 'texinfo';
161my $messages_textdomain = '@PACKAGE@';
162$messages_textdomain = 'texinfo' if ($messages_textdomain eq '@'.'PACKAGE@');
163my $strings_textdomain = '@PACKAGE@' . '_document';
164$strings_textdomain = 'texinfo_document'
165   if ($strings_textdomain eq '@'.'PACKAGE@' . '_document');
166
167
168# we want a reliable way to switch locale, so we don't use the system
169# gettext.
170Locale::Messages->select_package('gettext_pp');
171
172if ($Texinfo::ModulePath::texinfo_uninstalled) {
173  my $locales_dir = File::Spec->catdir($Texinfo::ModulePath::builddir,
174                                       'LocaleData');
175  if (-d $locales_dir) {
176    Locale::Messages::bindtextdomain ($strings_textdomain, $locales_dir);
177    # the messages in this domain are not regenerated automatically,
178    # only when calling ./maintain/regenerate_perl_module_files.sh
179    Locale::Messages::bindtextdomain ($messages_textdomain, $locales_dir);
180  } else {
181    warn "Locales dir for document strings not found\n";
182  }
183} else {
184  Locale::Messages::bindtextdomain ($strings_textdomain,
185                                    File::Spec->catdir($datadir, 'locale'));
186  Locale::Messages::bindtextdomain ($messages_textdomain,
187                                    File::Spec->catdir($datadir, 'locale'));
188}
189
190
191# Version setting is complicated, because we cope with
192# * script with configure values substituted or not
193# * script shipped as part of texinfo or as a standalone perl module
194
195# When shipped as a perl modules, $hardcoded_version is set to undef here
196# by a sed one liner.  The consequence is that configure.ac is not used
197# to retrieve the version number.
198# Otherwise this is only used as a safety value, and should never be used
199# in practice as a regexp extracts the version from configure.ac.
200my $hardcoded_version = "0.00-hardcoded";
201# Version set in configure.ac
202my $configured_version = '@PACKAGE_VERSION@';
203if ($configured_version eq '@' . 'PACKAGE_VERSION@') {
204  # if not configured, and $hardcoded_version is set search for the version
205  # in configure.ac
206  if (defined($hardcoded_version)) {
207    if (open (CONFIGURE,
208              "< ".File::Spec->catfile($Texinfo::ModulePath::top_srcdir,
209                                       'configure.ac'))) {
210      while (<CONFIGURE>) {
211        if (/^AC_INIT\(\[[^\]]+\]\s*,\s*\[([^\]]+)\]\s*,/) {
212          $configured_version = "$1+dev"; # +dev to distinguish from installed
213          last;
214        }
215      }
216      close (CONFIGURE);
217    }
218    # This should never be used, but is a safety value
219    $configured_version = $hardcoded_version if (!defined($configured_version));
220  } else {
221    # used in the standalone perl module, as $hardcoded_version is undef
222    # and it should never be configured in that setup
223    require Texinfo::Common;
224    $configured_version = $Texinfo::Common::VERSION;
225  }
226}
227
228# Compare the version of this file with the version of the modules
229# it is using.  If they are different, don't go any further.  This
230# can happen if multiple versions of texi2any are installed under a
231# different names, e.g. with the --program-suffix option to 'configure'.
232# The version in Common.pm is checked because that file has been present
233# since Texinfo 5.0 (the first release with texi2any in Perl).
234if ($configured_version ne $Texinfo::Common::VERSION
235    and $configured_version ne $Texinfo::Common::VERSION."+dev") {
236  warn "This is texi2any $configured_version but modules ".
237       "for texi2any $Texinfo::Common::VERSION found!\n";
238  die "Your installation of Texinfo is broken; aborting.\n";
239}
240
241
242my $configured_package = '@PACKAGE@';
243$configured_package = 'Texinfo' if ($configured_package eq '@' . 'PACKAGE@');
244my $configured_name = '@PACKAGE_NAME@';
245$configured_name = $configured_package
246  if ($configured_name eq '@' .'PACKAGE_NAME@');
247my $configured_name_version = "$configured_name $configured_version";
248my $configured_url = '@PACKAGE_URL@';
249$configured_url = 'http://www.gnu.org/software/texinfo/'
250  if ($configured_url eq '@' .'PACKAGE_URL@');
251
252my $texinfo_dtd_version = '@TEXINFO_DTD_VERSION@';
253# $hardcoded_version is undef for a standalone perl module
254if ($texinfo_dtd_version eq '@' . 'TEXINFO_DTD_VERSION@') {
255  $texinfo_dtd_version = undef;
256  if (defined($hardcoded_version)) {
257    if (open (CONFIGURE,
258            "< ".File::Spec->catfile($Texinfo::ModulePath::top_srcdir,
259                                     'configure.ac'))) {
260      while (<CONFIGURE>) {
261        if (/^TEXINFO_DTD_VERSION=([0-9]\S*)/) {
262          $texinfo_dtd_version = "$1";
263          last;
264        }
265      }
266      close (CONFIGURE);
267    }
268  }
269}
270# Used in case it is not hardcoded in configure and for standalone perl module
271$texinfo_dtd_version = $configured_version
272  if (!defined($texinfo_dtd_version));
273
274# defaults for options relevant in the main program, not undef, and also
275# defaults for all the converters.
276# Other relevant options (undef) are NO_WARN FORCE OUTFILE
277# Others are set in the converters (FORMAT_MENU).
278my $converter_default_options = {
279    'ERROR_LIMIT' => 100,
280    'TEXI2DVI' => 'texi2dvi',
281    'PACKAGE_VERSION' => $configured_version,
282    'PACKAGE' => $configured_package,
283    'PACKAGE_NAME' => $configured_name,
284    'PACKAGE_AND_VERSION' => $configured_name_version,
285    'PACKAGE_URL' => $configured_url,
286    'PROGRAM' => $real_command_name,
287    'TEXINFO_DTD_VERSION' => $texinfo_dtd_version,
288};
289
290# determine configuration directories.
291
292my $conf_file_name = 'Config' ;
293
294# directories for texinfo configuration files
295my @language_config_dirs = File::Spec->catdir($curdir, '.texinfo');
296push @language_config_dirs, File::Spec->catdir($ENV{'HOME'}, '.texinfo')
297                                if (defined($ENV{'HOME'}));
298push @language_config_dirs, File::Spec->catdir($sysconfdir, 'texinfo')
299                               if (defined($sysconfdir));
300push @language_config_dirs, File::Spec->catdir($datadir, 'texinfo')
301                               if (defined($datadir));
302my @texinfo_config_dirs = ($curdir, @language_config_dirs);
303
304my @program_config_dirs;
305my @program_init_dirs;
306
307my $program_name = 'texi2any';
308@program_config_dirs = ($curdir, File::Spec->catdir($curdir, ".$program_name"));
309push @program_config_dirs, File::Spec->catdir($ENV{'HOME'}, ".$program_name")
310       if (defined($ENV{'HOME'}));
311push @program_config_dirs, File::Spec->catdir($sysconfdir, $program_name)
312       if (defined($sysconfdir));
313push @program_config_dirs, File::Spec->catdir($datadir, $program_name)
314  if (defined($datadir));
315
316@program_init_dirs = @program_config_dirs;
317foreach my $texinfo_config_dir (@language_config_dirs) {
318  push @program_init_dirs, File::Spec->catdir($texinfo_config_dir, 'init');
319}
320
321# Namespace for configuration
322{
323package Texinfo::Config;
324
325#use Carp;
326
327# passed from main program
328my $cmdline_options;
329my $default_options;
330# used in main program
331our $options = {};
332
333sub _load_config($$) {
334  $default_options = shift;
335  $cmdline_options = shift;
336  #print STDERR "cmdline_options: ".join('|',keys(%$cmdline_options))."\n";
337}
338
339sub _load_init_file($) {
340  my $file = shift;
341  require Texinfo::Convert::HTML;
342  eval { require($file) ;};
343  my $e = $@;
344  if ($e ne '') {
345    main::document_warn(sprintf(main::__("error loading %s: %s\n"),
346                                 $file, $e));
347  }
348}
349
350# FIXME: maybe use an opaque return status that can be used to retrieve
351# an error message?
352sub set_from_init_file($$) {
353  my $var = shift;
354  my $value = shift;
355  if (!Texinfo::Common::valid_option($var)) {
356    # carp may be better, but infortunately, it points to the routine that
357    # loads the file, and not to the init file.
358    main::document_warn(sprintf(main::__("%s: unknown variable %s"),
359                                'set_from_init_file', $var));
360    return 0;
361  }
362  return 0 if (defined($cmdline_options->{$var}));
363  delete $default_options->{$var};
364  $options->{$var} = $value;
365  return 1;
366}
367
368sub set_from_cmdline($$) {
369  my $var = shift;
370  my $value = shift;
371  delete $options->{$var};
372  delete $default_options->{$var};
373  if (!Texinfo::Common::valid_option($var)) {
374    main::document_warn(sprintf(main::__("%s: unknown variable %s\n"),
375                                'set_from_cmdline', $var));
376    return 0;
377  }
378  $cmdline_options->{$var} = $value;
379  return 1;
380}
381
382# This also could get and set some @-command results.
383# FIXME But it does not take into account what happens during conversion,
384# for that something like $converter->get_conf(...) has to be used.
385sub get_conf($) {
386  my $var = shift;
387  if (exists($cmdline_options->{$var})) {
388    return $cmdline_options->{$var};
389  } elsif (exists($options->{$var})) {
390    return $options->{$var};
391  } elsif (exists($default_options->{$var})) {
392    return $default_options->{$var};
393  } else {
394    return undef;
395  }
396}
397
398# to dynamically add options from init files
399sub texinfo_add_valid_option($)
400{
401  my $option = shift;
402  return Texinfo::Common::add_valid_option($option);
403}
404
405}
406# back in main program namespace
407
408sub locate_and_load_init_file($$)
409{
410  my $filename = shift;
411  my $directories = shift;
412
413  my $file = Texinfo::Common::locate_init_file($filename, $directories, 0);
414  if (defined($file)) {
415    Texinfo::Config::_load_init_file($file);
416  } else {
417    document_warn(sprintf(__("could not read init file %s"), $filename));
418  }
419}
420
421# read initialization files
422foreach my $file (Texinfo::Common::locate_init_file($conf_file_name,
423                  [ reverse(@program_config_dirs) ], 1)) {
424  Texinfo::Config::_load_init_file($file);
425}
426
427sub set_from_cmdline($$) {
428  return &Texinfo::Config::set_from_cmdline(@_);
429}
430
431sub set_from_init_file($$) {
432  return &Texinfo::Config::set_from_init_file(@_);
433}
434
435sub get_conf($) {
436  return &Texinfo::Config::get_conf(@_);
437}
438
439my @input_file_suffixes = ('.txi','.texinfo','.texi','.txinfo','');
440
441my @texi2dvi_args = ();
442
443my $format = 'info';
444# this is the format associated with the output format, which is replaced
445# when the output format changes.  It may also be removed if there is the
446# corresponding --no-ifformat.
447my $default_expanded_format = [ $format ];
448my @conf_dirs = ();
449my @include_dirs = ();
450my @prepend_dirs = ();
451
452# options for all the files
453my $parser_options = {'expanded_formats' => [],
454                              'values' => {'txicommandconditionals' => 1}};
455
456Texinfo::Config::_load_config($converter_default_options, $cmdline_options);
457
458sub set_expansion($$) {
459  my $region = shift;
460  my $set = shift;
461  $set = 1 if (!defined($set));
462  if ($set) {
463    push @{$parser_options->{'expanded_formats'}}, $region
464      unless (grep {$_ eq $region} @{$parser_options->{'expanded_formats'}});
465  } else {
466    @{$parser_options->{'expanded_formats'}} =
467      grep {$_ ne $region} @{$parser_options->{'expanded_formats'}};
468    @{$default_expanded_format}
469       = grep {$_ ne $region} @{$default_expanded_format};
470  }
471}
472
473my $format_from_command_line = 0;
474
475my %format_command_line_names = (
476  'xml' => 'texinfoxml',
477);
478
479my %formats_table = (
480 'info' => {
481             'nodes_tree' => 1,
482             'floats' => 1,
483             'module' => 'Texinfo::Convert::Info'
484           },
485  'plaintext' => {
486             'nodes_tree' => 1,
487             'floats' => 1,
488             'split' => 1,
489             'module' => 'Texinfo::Convert::Plaintext'
490           },
491  'html' => {
492             'nodes_tree' => 1,
493             'floats' => 1,
494             'split' => 1,
495             'internal_links' => 1,
496             'simple_menu' => 1,
497             'move_index_entries_after_items' => 1,
498             'relate_index_entries_to_table_entries' => 1,
499             'no_warn_non_empty_parts' => 1,
500             'module' => 'Texinfo::Convert::HTML'
501           },
502  'texinfoxml' => {
503             'nodes_tree' => 1,
504             'module' => 'Texinfo::Convert::TexinfoXML',
505             'floats' => 1,
506           },
507  'texinfosxml' => {
508             'nodes_tree' => 1,
509             'module' => 'Texinfo::Convert::TexinfoSXML',
510             'floats' => 1,
511           },
512  'ixinsxml' => {
513             'nodes_tree' => 1,
514             'module' => 'Texinfo::Convert::IXINSXML'
515           },
516  'docbook' => {
517             'move_index_entries_after_items' => 1,
518             'no_warn_non_empty_parts' => 1,
519             'module' => 'Texinfo::Convert::DocBook'
520           },
521  'pdf' => {
522             'texi2dvi_format' => 1,
523           },
524  'ps' =>  {
525             'texi2dvi_format' => 1,
526           },
527  'dvi' => {
528             'texi2dvi_format' => 1,
529           },
530  'dvipdf' => {
531             'texi2dvi_format' => 1,
532           },
533  'debugtree' => {
534          'split' => 1,
535          'module' => 'DebugTexinfo::DebugTree'
536         },
537  'textcontent' => {
538            'module' => 'Texinfo::Convert::TextContent'
539           },
540  'rawtext' => {
541            'module' => 'Texinfo::Convert::Text'
542           },
543  'plaintexinfo' => {
544            'module' => 'Texinfo::Convert::PlainTexinfo'
545           },
546  'parse' => {
547           },
548  'structure' => {
549             'nodes_tree' => 1,
550             'floats' => 1,
551             'split' => 1,
552           },
553);
554
555my $call_texi2dvi = 0;
556
557# previous_format should be in argument if there is a possibility of error.
558# as a fallback, the $format global variable is used.
559sub set_format($;$$)
560{
561  my $set_format = shift;
562  my $previous_format = shift;
563  $previous_format = $format if (!defined($previous_format));
564  my $do_not_override_command_line = shift;
565
566  my $new_format;
567  if ($format_command_line_names{$set_format}) {
568    $new_format = $format_command_line_names{$set_format};
569  } else {
570    $new_format = $set_format;
571  }
572  my $expanded_format = $set_format;
573  if (!$formats_table{$new_format}) {
574    document_warn(sprintf(__("ignoring unrecognized TEXINFO_OUTPUT_FORMAT value `%s'\n"),
575                 $new_format));
576    $new_format = $previous_format;
577  } else {
578    if ($format_from_command_line and $do_not_override_command_line) {
579      $new_format = $previous_format;
580    } else {
581      if ($formats_table{$new_format}->{'texi2dvi_format'}) {
582        $call_texi2dvi = 1;
583        push @texi2dvi_args, '--'.$new_format;
584        $expanded_format = 'tex';
585      }
586      if ($Texinfo::Common::texinfo_output_formats{$expanded_format}) {
587        if ($expanded_format eq 'plaintext') {
588          $default_expanded_format = [$expanded_format, 'info']
589        } else {
590          $default_expanded_format = [$expanded_format]
591        }
592      }
593      $format_from_command_line = 1
594        unless ($do_not_override_command_line);
595    }
596  }
597  return $new_format;
598}
599
600sub set_global_format($)
601{
602  my $set_format = shift;
603  $format = set_format($set_format);
604}
605
606sub document_warn($) {
607  return if (get_conf('NO_WARN'));
608  my $text = shift;
609  chomp ($text);
610  warn(sprintf(__p("program name: warning: warning_message",
611                   "%s: warning: %s\n"), $real_command_name,  $text));
612}
613
614sub _exit($$)
615{
616  my $error_count = shift;
617  my $opened_files = shift;
618
619  if ($error_count and $opened_files and !get_conf('FORCE')) {
620    while (@$opened_files) {
621      my $opened_file = shift (@$opened_files);
622      unlink ($opened_file);
623    }
624  }
625  exit (1) if ($error_count and (!get_conf('FORCE')
626     or $error_count > get_conf('ERROR_LIMIT')));
627}
628
629sub handle_errors($$$)
630{
631  my $self = shift;
632  my $error_count = shift;
633  my $opened_files = shift;
634  my ($errors, $new_error_count) = $self->errors();
635  $error_count += $new_error_count if ($new_error_count);
636  foreach my $error_message (@$errors) {
637    warn $error_message->{'error_line'} if ($error_message->{'type'} eq 'error'
638                                           or !get_conf('NO_WARN'));
639  }
640
641  _exit($error_count, $opened_files);
642  return $error_count;
643}
644
645
646sub _get_converter_default($)
647{
648  my $option = shift;
649  return $Texinfo::Convert::Converter::all_converters_defaults{$option}
650   if (defined($Texinfo::Convert::Converter::all_converters_defaults{$option}));
651  return undef;
652}
653
654# translation related todo to be done when the string change anyway to
655# avoid requiring translation
656sub makeinfo_help()
657{
658  # TODO: avoid \n in translated strings.  Report from Benno Schulenberg
659  my $makeinfo_help =
660    sprintf(__("Usage: %s [OPTION]... TEXINFO-FILE...\n"),
661    $real_command_name . $command_suffix)
662."\n".
663__("Translate Texinfo source documentation to various other formats, by default
664Info files suitable for reading online with Emacs or standalone GNU Info.
665
666This program is commonly installed as both `makeinfo' and `texi2any';
667the behavior is identical, and does not depend on the installed name.\n")
668."\n";
669  # TODO: avoid \n in translated strings, split each option in a translatable
670  # string.  Report from Benno Schulenberg
671  $makeinfo_help .= sprintf(__("General options:
672      --document-language=STR locale to use in translating Texinfo keywords
673                                for the output document (default C).
674      --error-limit=NUM       quit after NUM errors (default %d).
675      --force                 preserve output even if errors.
676      --help                  display this help and exit.
677      --no-validate           suppress node cross-reference validation.
678      --no-warn               suppress warnings (but not errors).
679      --conf-dir=DIR          search also for initialization files in DIR.
680      --init-file=FILE        load FILE to modify the default behavior.
681  -c, --set-customization-variable VAR=VAL  set customization variable VAR
682                                to value VAL.
683  -v, --verbose               explain what is being done.
684      --version               display version information and exit.\n"),
685    get_conf('ERROR_LIMIT'))
686."\n";
687  # TODO: avoid \n in translated strings, split each option in a translatable
688  # string.  Report from Benno Schulenberg
689  $makeinfo_help .= __("Output format selection (default is to produce Info):
690      --docbook               output Docbook XML rather than Info.
691      --html                  output HTML rather than Info.
692      --plaintext             output plain text rather than Info.
693      --xml                   output Texinfo XML rather than Info.
694      --dvi, --dvipdf, --ps, --pdf  call texi2dvi to generate given output,
695                                after checking validity of TEXINFO-FILE.\n")
696."\n";
697  # TODO: avoid \n in translated strings, split each option in a translatable
698  # string.  Report from Benno Schulenberg
699  $makeinfo_help .= __("General output options:
700  -E, --macro-expand=FILE     output macro-expanded source to FILE,
701                                ignoring any \@setfilename.
702      --no-headers            suppress node separators, Node: lines, and menus
703                                from Info output (thus producing plain text)
704                                or from HTML (thus producing shorter output).
705                                Also, if producing Info, write to
706                                standard output by default.
707      --no-split              suppress any splitting of the output;
708                                generate only one output file.
709      --[no-]number-sections  output chapter and sectioning numbers;
710                                default is on.
711  -o, --output=DEST           output to DEST.
712                                With split output, create DEST as a directory
713                                 and put the output files there.
714                                With non-split output, if DEST is already
715                                 a directory or ends with a /,
716                                 put the output file there.
717                                Otherwise, DEST names the output file.\n")
718."\n";
719  # TODO: avoid \n in translated strings, split each option in a translatable
720  # string.  Report from Benno Schulenberg
721  $makeinfo_help .= sprintf(__("Options for Info and plain text:
722      --disable-encoding      do not output accented and special characters
723                                in Info output based on \@documentencoding.
724      --enable-encoding       override --disable-encoding (default).
725      --fill-column=NUM       break Info lines at NUM characters (default %d).
726      --footnote-style=STYLE  output footnotes in Info according to STYLE:
727                                `separate' to put them in their own node;
728                                `end' to put them at the end of the node, in
729                                which they are defined (this is the default).
730      --paragraph-indent=VAL  indent Info paragraphs by VAL spaces (default %d).
731                                If VAL is `none', do not indent; if VAL is
732                                `asis', preserve existing indentation.
733      --split-size=NUM        split Info files at size NUM (default %d).\n"),
734    _get_converter_default('fillcolumn'),
735    _get_converter_default('paragraphindent'),
736    _get_converter_default('SPLIT_SIZE'))
737."\n";
738  # TODO: avoid \n in translated strings, split each option in a translatable
739  # string.  Report from Benno Schulenberg
740  $makeinfo_help .= __("Options for HTML:
741      --css-include=FILE      include FILE in HTML <style> output;
742                                read stdin if FILE is -.
743      --css-ref=URL           generate CSS reference to URL.
744      --internal-links=FILE   produce list of internal links in FILE.
745      --split=SPLIT           split at SPLIT, where SPLIT may be `chapter',
746                                `section' or `node'.
747      --transliterate-file-names  use file names in ASCII transliteration.
748      --node-files            produce redirection files for nodes and
749                                anchors; default is set only if split.\n")
750."\n";
751  # TODO: avoid \n in translated strings.  Report from Benno Schulenberg
752  $makeinfo_help .= __("Options for XML and Docbook:
753      --output-indent=VAL     does nothing, retained for compatibility.\n")
754."\n";
755  $makeinfo_help .= __("Options for DVI/PS/PDF:
756      --Xopt=OPT              pass OPT to texi2dvi; can be repeated.\n")
757."\n";
758  # TODO: avoid \n in translated strings, split each option in a translatable
759  # string.  Report from Benno Schulenberg
760  $makeinfo_help .= __("Input file options:
761      --commands-in-node-names  does nothing, retained for compatibility.
762  -D VAR                        define the variable VAR, as with \@set.
763  -D 'VAR VAL'                  define VAR to VAL (one shell argument).
764  -I DIR                        append DIR to the \@include search path.
765  -P DIR                        prepend DIR to the \@include search path.
766  -U VAR                        undefine the variable VAR, as with \@clear.\n")
767."\n";
768  # TODO: avoid \n in translated strings, split each option in a translatable
769  # string.  Report from Benno Schulenberg
770  $makeinfo_help .= __("Conditional processing in input:
771  --ifdocbook       process \@ifdocbook and \@docbook even if
772                      not generating Docbook.
773  --ifhtml          process \@ifhtml and \@html even if not generating HTML.
774  --ifinfo          process \@ifinfo even if not generating Info.
775  --ifplaintext     process \@ifplaintext even if not generating plain text.
776  --iftex           process \@iftex and \@tex.
777  --ifxml           process \@ifxml and \@xml.
778  --no-ifdocbook    do not process \@ifdocbook and \@docbook text.
779  --no-ifhtml       do not process \@ifhtml and \@html text.
780  --no-ifinfo       do not process \@ifinfo text.
781  --no-ifplaintext  do not process \@ifplaintext text.
782  --no-iftex        do not process \@iftex and \@tex text.
783  --no-ifxml        do not process \@ifxml and \@xml text.
784
785  Also, for the --no-ifFORMAT options, do process \@ifnotFORMAT text.\n")
786."\n";
787  # TODO: avoid \n in translated strings, split each option in a translatable
788  # string.  Report from Benno Schulenberg
789  $makeinfo_help .= __("  The defaults for the \@if... conditionals depend on the output format:
790  if generating Docbook, --ifdocbook is on and the others are off;
791  if generating HTML, --ifhtml is on and the others are off;
792  if generating Info, --ifinfo is on and the others are off;
793  if generating plain text, --ifplaintext is on and the others are off;
794  if generating XML, --ifxml is on and the others are off.\n")
795."\n";
796  # TODO: avoid \n in translated strings, split each option in a translatable
797  # string.  Report from Benno Schulenberg
798  $makeinfo_help .= __("Examples:
799  makeinfo foo.texi                      write Info to foo's \@setfilename
800  makeinfo --html foo.texi               write HTML to \@setfilename
801  makeinfo --xml foo.texi                write Texinfo XML to \@setfilename
802  makeinfo --docbook foo.texi            write Docbook XML to \@setfilename
803  makeinfo --plaintext foo.texi          write plain text to standard output
804  makeinfo --pdf foo.texi                write PDF using texi2dvi
805
806  makeinfo --html --no-headers foo.texi  write html without node lines, menus
807  makeinfo --number-sections foo.texi    write Info with numbered sections
808  makeinfo --no-split foo.texi           write one Info file however big\n")
809."\n";
810  $makeinfo_help .= __("Email bug reports to bug-texinfo\@gnu.org,
811general questions and discussion to help-texinfo\@gnu.org.
812Texinfo home page: http://www.gnu.org/software/texinfo/") ."\n";
813  return $makeinfo_help;
814}
815
816my $Xopt_arg_nr = 0;
817
818my $result_options = Getopt::Long::GetOptions (
819 'help|h' => sub { print makeinfo_help(); exit 0; },
820 'version|V' => sub {print "$program_name (GNU texinfo) $configured_version\n\n";
821    printf __("Copyright (C) %s Free Software Foundation, Inc.
822License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
823This is free software: you are free to change and redistribute it.
824There is NO WARRANTY, to the extent permitted by law.\n"), "2021";
825      exit 0;},
826 'macro-expand|E=s' => sub { set_from_cmdline('MACRO_EXPAND', $_[1]); },
827 'ifhtml!' => sub { set_expansion('html', $_[1]); },
828 'ifinfo!' => sub { set_expansion('info', $_[1]); },
829 'ifxml!' => sub { set_expansion('xml', $_[1]); },
830 'ifdocbook!' => sub { set_expansion('docbook', $_[1]); },
831 'iftex!' => sub { set_expansion('tex', $_[1]); },
832 'ifplaintext!' => sub { set_expansion('plaintext', $_[1]); },
833 'I=s' => sub { push @texi2dvi_args, ('-'.$_[0], $_[1]);
834                push @include_dirs, split(/$quoted_path_separator/, $_[1]); },
835 'conf-dir=s' => sub { push @conf_dirs, split(/$quoted_path_separator/, $_[1]); },
836 'P=s' => sub { unshift @prepend_dirs, split(/$quoted_path_separator/, $_[1]); },
837 'number-sections!' => sub { set_from_cmdline('NUMBER_SECTIONS', $_[1]); },
838 'number-footnotes!' => sub { set_from_cmdline('NUMBER_FOOTNOTES', $_[1]); },
839 'node-files!' => sub { set_from_cmdline('NODE_FILES', $_[1]); },
840 'footnote-style=s' => sub {
841    if ($_[1] eq 'end' or $_[1] eq 'separate') {
842       set_from_cmdline('footnotestyle', $_[1]);
843    } else {
844      die sprintf(__("%s: --footnote-style arg must be `separate' or `end', not `%s'.\n"), $real_command_name, $_[1]);
845    }
846  },
847 'split=s' => sub {  my $split = $_[1];
848                     my @messages
849                       = Texinfo::Common::warn_unknown_split($_[1]);
850                     if (@messages) {
851                       foreach my $message (@messages) {
852                         document_warn($message);
853                       }
854                       $split = 'node';
855                     }
856                     set_from_cmdline('SPLIT', $split); },
857 'no-split' => sub { set_from_cmdline('SPLIT', '');
858                     set_from_cmdline('SPLIT_SIZE', undef);},
859 'headers!' => sub { set_from_cmdline('HEADERS', $_[1]);
860                     if (!$_[1]) {
861                       set_from_cmdline('FORMAT_MENU', 'nomenu');
862                     } else {
863                       # a special value that is modified below when the
864                       # output format is known, to be the default for that
865                       # format, or 'menu'
866                       set_from_cmdline('FORMAT_MENU', 'set_format_menu_from_cmdline_header');
867                     }
868                     $format = 'plaintext' if (!$_[1] and $format eq 'info'); },
869 'output|out|o=s' => sub {
870    my $var = 'OUTFILE';
871    if ($_[1] =~ m:/$: or -d $_[1]) {
872      set_from_cmdline($var, undef);
873      $var = 'SUBDIR';
874    }
875    set_from_cmdline($var, $_[1]);
876    push @texi2dvi_args, '-o', $_[1];
877  },
878 'no-validate|no-pointer-validate' => sub {
879      set_from_cmdline('novalidate',$_[1]);
880      $parser_options->{'info'}->{'novalidate'} = $_[1];
881    },
882 'no-warn' => sub { set_from_cmdline('NO_WARN', $_[1]); },
883 'verbose|v!' => sub {set_from_cmdline('VERBOSE', $_[1]);
884                     push @texi2dvi_args, '--verbose'; },
885 'document-language=s' => sub {
886                      set_from_cmdline('documentlanguage', $_[1]);
887                      $parser_options->{'documentlanguage'} = $_[1];
888                      my @messages
889                       = Texinfo::Common::warn_unknown_language($_[1]);
890                      foreach my $message (@messages) {
891                        document_warn($message);
892                      }
893                    },
894 'D=s' => sub {
895    my $var = $_[1];
896    my @field = split (/\s+/, $var, 2);
897    if (@field == 1) {
898      $parser_options->{'values'}->{$var} = 1;
899      push @texi2dvi_args, "--command=\@set $var 1";
900    } else {
901      $parser_options->{'values'}->{$field[0]} = $field[1];
902      push @texi2dvi_args, "--command=\@set $field[0] $field[1]";
903    }
904 },
905 'U=s' => sub {
906    delete $parser_options->{'values'}->{$_[1]};
907    push @texi2dvi_args, "--command=\@clear $_[1]";
908 },
909 'init-file=s' => sub {
910    locate_and_load_init_file($_[1], [ @conf_dirs, @program_init_dirs ]);
911 },
912 'set-customization-variable|c=s' => sub {
913   my $var_val = $_[1];
914   if ($var_val =~ s/^(\w+)\s*=?\s*//) {
915     my $var = $1;
916     my $value = $var_val;
917     if ($value =~ /^undef$/i) {
918       $value = undef;
919     }
920     set_from_cmdline($var, $value);
921   }
922 },
923 'css-include=s' => \@css_files,
924 'css-ref=s' => \@css_refs,
925 'transliterate-file-names!' =>
926     sub {set_from_cmdline('TRANSLITERATE_FILE_NAMES', $_[1]);},
927 'error-limit|e=i' => sub { set_from_cmdline('ERROR_LIMIT', $_[1]); },
928 'split-size=s' => sub {set_from_cmdline('SPLIT_SIZE', $_[1])},
929 'paragraph-indent|p=s' => sub {
930    my $value = $_[1];
931    if ($value =~ /^([0-9]+)$/ or $value eq 'none' or $value eq 'asis') {
932      set_from_cmdline('paragraphindent', $_[1]);
933    } else {
934      die sprintf(__("%s: --paragraph-indent arg must be numeric/`none'/`asis', not `%s'.\n"),
935                  $real_command_name, $value);
936    }
937 },
938 'fill-column|f=i' => sub {set_from_cmdline('FILLCOLUMN',$_[1]);},
939 'enable-encoding' => sub {set_from_cmdline('ENABLE_ENCODING',$_[1]);
940                     $parser_options->{'ENABLE_ENCODING'} = $_[1];},
941 'disable-encoding' => sub {set_from_cmdline('ENABLE_ENCODING', 0);
942                     $parser_options->{'ENABLE_ENCODING'} = 0;},
943 'internal-links=s' => sub {set_from_cmdline('INTERNAL_LINKS', $_[1]);},
944 'force|F' => sub {set_from_cmdline('FORCE', $_[1]);},
945 'commands-in-node-names' => sub { ;},
946 'output-indent=i' => sub { ;},
947 'reference-limit=i' => sub { ;},
948 'Xopt=s' => sub {push @texi2dvi_args, $_[1]; $Xopt_arg_nr++},
949 'silent|quiet' => sub { push @texi2dvi_args, '--'.$_[0];},
950 'plaintext' => sub {$format = set_format($_[0].'');},
951 'html' => sub {$format = set_format($_[0].'');},
952 'info' => sub {$format = set_format($_[0].'');},
953 'docbook' => sub {$format = set_format($_[0].'');},
954 'xml' => sub {$format = set_format($_[0].'');},
955 'dvi' => sub {$format = set_format($_[0].'');},
956 'dvipdf' => sub {$format = set_format($_[0].'');},
957 'ps' => sub {$format = set_format($_[0].'');},
958 'pdf' => sub {$format = set_format($_[0].'');},
959 'debug=i' => sub {set_from_cmdline('DEBUG', $_[1]);
960                   $parser_options->{'DEBUG'} = $_[1];
961                   push @texi2dvi_args, '--'.$_[0]; },
962);
963
964
965
966exit 1 if (!$result_options);
967
968# Change some options depending on the settings of other ones
969sub normalize_config {
970  my $conf = shift;
971
972  if (defined($conf->{'TEXINFO_OUTPUT_FORMAT'})) {
973    $format = set_format($conf->{'TEXINFO_OUTPUT_FORMAT'}, $format, 1);
974  } elsif (defined($conf->{'TEXI2HTML'})) {
975    $format = set_format('html', $format, 1);
976    $parser_options->{'values'}->{'texi2html'} = 1;
977  }
978  if (defined($conf->{'HTML_MATH'}) and $conf->{'HTML_MATH'} eq 'l2h') {
979    $conf->{'L2H'} = 1;
980  }
981}
982
983$cmdline_options->{'include_directories'} = [@include_dirs];
984
985normalize_config($cmdline_options);
986
987# FIXME do this here or inside format-specific code?
988my $latex2html_file = 'latex2html.pm';
989if (defined($cmdline_options->{'L2H'})) {
990  locate_and_load_init_file($latex2html_file,
991                        [ @conf_dirs, @program_init_dirs ]);
992}
993
994my $tex4ht_file = 'tex4ht.pm';
995if (defined($cmdline_options->{'HTML_MATH'})
996      and $cmdline_options->{'HTML_MATH'} eq 't4h') {
997  locate_and_load_init_file($tex4ht_file,
998                        [ @conf_dirs, @program_init_dirs ]);
999}
1000
1001# For tests, set some strings to values not changing with releases
1002my %test_conf = (
1003    'PACKAGE_VERSION' => '',
1004    'PACKAGE' => 'texinfo',
1005    'PACKAGE_NAME' => 'texinfo',
1006    'PACKAGE_AND_VERSION' => 'texinfo',
1007    'PACKAGE_URL' => 'http://www.gnu.org/software/texinfo/',
1008# maybe don't set this?
1009    'PROGRAM' => 'texi2any',
1010);
1011if (get_conf('TEST')) {
1012  foreach my $conf (keys (%test_conf)) {
1013    $converter_default_options->{$conf} = $test_conf{$conf};
1014  }
1015}
1016
1017
1018my %format_names = (
1019 'info' => 'Info',
1020 'html' => 'HTML',
1021 'docbook' => 'DocBook',
1022 'texinfoxml' => 'Texinfo XML',
1023 'plaintext' => 'Plain Text',
1024);
1025
1026sub format_name($)
1027{
1028  my $format = shift;
1029  if ($format_names{$format}) {
1030    return $format_names{$format};
1031  } else {
1032    return $format;
1033  }
1034}
1035
1036
1037if (defined($ENV{'TEXINFO_OUTPUT_FORMAT'})
1038    and $ENV{'TEXINFO_OUTPUT_FORMAT'} ne '') {
1039  $format = set_format($ENV{'TEXINFO_OUTPUT_FORMAT'}, $format, 1);
1040}
1041
1042if ($call_texi2dvi) {
1043  if (defined(get_conf('OUTFILE')) and @ARGV > 1) {
1044    die sprintf(__('%s: when generating %s, only one input FILE may be specified with -o'."\n"),
1045                $real_command_name, format_name($format));
1046  }
1047} elsif($Xopt_arg_nr) {
1048  document_warn(__('--Xopt option without printed output'));
1049}
1050
1051require Texinfo::Parser;
1052require Texinfo::Structuring;
1053require Texinfo::Transformations;
1054# Avoid loading these modules until down here to speed up the case
1055# when they are not needed.
1056
1057my %tree_transformations;
1058if (get_conf('TREE_TRANSFORMATIONS')) {
1059  my @transformations = split /,/, get_conf('TREE_TRANSFORMATIONS');
1060  foreach my $transformation (@transformations) {
1061    if (Texinfo::Common::valid_tree_transformation($transformation)) {
1062      $tree_transformations{$transformation} = 1;
1063    } else {
1064      document_warn(sprintf(__('unknown tree transformation %s'),
1065                     $transformation));
1066    }
1067  }
1068}
1069
1070if (get_conf('SPLIT') and !$formats_table{$format}->{'split'}) {
1071  document_warn(sprintf(__('ignoring splitting for format %s'),
1072                        format_name($format)));
1073  set_from_cmdline('SPLIT', '');
1074}
1075
1076foreach my $expanded_format (@{$default_expanded_format}) {
1077  push @{$parser_options->{'expanded_formats'}}, $expanded_format
1078    unless (grep {$_ eq $expanded_format} @{$parser_options->{'expanded_formats'}});
1079}
1080
1081my $converter_class;
1082my %converter_defaults;
1083
1084if (defined($formats_table{$format}->{'module'})) {
1085  # Speed up initialization by only loading the module we need.
1086  eval "require $formats_table{$format}->{'module'};"
1087      or die "$@";
1088  eval '$formats_table{$format}->{\'converter\'} = sub{'.
1089                $formats_table{$format}->{'module'}
1090        .'->converter(@_)};';
1091}
1092
1093if (defined($formats_table{$format}->{'module'})) {
1094  $converter_class = $formats_table{$format}->{'module'};
1095  # $cmdline_options is passed to have TEXI2HTML set for conversion to
1096  # HTML
1097  %converter_defaults = $converter_class->converter_defaults($cmdline_options);
1098
1099  # set FORMAT_MENU to the output format default, if not nomenu
1100  if (defined(get_conf('FORMAT_MENU'))
1101      and get_conf('FORMAT_MENU') eq 'set_format_menu_from_cmdline_header') {
1102    if (defined($converter_defaults{'FORMAT_MENU'})
1103        and $converter_defaults{'FORMAT_MENU'} ne 'nomenu') {
1104      set_from_cmdline('FORMAT_MENU', $converter_defaults{'FORMAT_MENU'});
1105    } else {
1106      set_from_cmdline('FORMAT_MENU', 'menu');
1107    }
1108  }
1109} else {
1110  if (defined(get_conf('FORMAT_MENU'))
1111      and get_conf('FORMAT_MENU') eq 'set_format_menu_from_cmdline_header') {
1112    set_from_cmdline('FORMAT_MENU', 'menu');
1113  }
1114}
1115
1116# using no warnings is wrong, but a way to avoid a spurious warning.
1117no warnings 'once';
1118my @parser_settable_options = keys(%Texinfo::Common::default_parser_customization_values);
1119push @parser_settable_options, keys(%Texinfo::Common::default_structure_customization_values);
1120foreach my $parser_settable_option (@parser_settable_options) {
1121  if (defined(get_conf($parser_settable_option))) {
1122    $parser_options->{$parser_settable_option}
1123       = get_conf($parser_settable_option);
1124  } elsif (defined($converter_class)
1125           and defined($converter_defaults{$parser_settable_option})) {
1126    $parser_options->{$parser_settable_option}
1127       = $converter_defaults{$parser_settable_option};
1128  }
1129}
1130
1131# Copy some of the customization variables into the parser options.
1132# The configuration options are upper-cased when considered as
1133# customization variables, and lower-cased when passed to the Parser.
1134# The customization variables passed here can only be set in perl
1135# customization files, using set_from_init_file().
1136foreach my $parser_option (map {uc($_)}
1137                  (keys (%Texinfo::Common::default_parser_state_configuration))) {
1138  $parser_options->{lc($parser_option)} = get_conf($parser_option)
1139    if (defined(get_conf($parser_option)));
1140}
1141
1142
1143# Main processing, process all the files given on the command line
1144
1145my @input_files = @ARGV;
1146# use STDIN if not a tty, like makeinfo does
1147@input_files = ('-') if (!scalar(@input_files) and !-t STDIN);
1148die sprintf(__("%s: missing file argument.\n"), $real_command_name)
1149   .sprintf(__("Try `%s --help' for more information.\n"), $real_command_name)
1150     unless (scalar(@input_files) >= 1);
1151
1152my $file_number = -1;
1153my @opened_files = ();
1154my %unclosed_files;
1155my $error_count = 0;
1156# main processing
1157while(@input_files) {
1158  $file_number++;
1159  my $input_file_arg = shift(@input_files);
1160  my $input_file_name;
1161  # try to concatenate with different suffixes. The last suffix is ''
1162  # such that the plain file name is checked.
1163  foreach my $suffix (@input_file_suffixes) {
1164    if (-e $input_file_arg.$suffix) {
1165      $input_file_name = $input_file_arg.$suffix;
1166      last;
1167    }
1168  }
1169  # in case no file was found, still set the file name
1170  $input_file_name = $input_file_arg if (!defined($input_file_name));
1171
1172  my ($input_filename, $input_directory, $suffix) = fileparse($input_file_name);
1173  if (!defined($input_directory) or $input_directory eq '') {
1174    $input_directory = $curdir;
1175  }
1176
1177  my $input_file_base = $input_file_name;
1178  $input_file_base =~ s/\.te?x(i|info)?$//;
1179
1180  my $parser_file_options = { %$parser_options };
1181
1182  $parser_file_options->{'include_directories'} = [@include_dirs];
1183
1184  my @prepended_include_directories = ('.');
1185  push @prepended_include_directories, $input_directory
1186      if ($input_directory ne '.');
1187  @prepended_include_directories =
1188    (@prepend_dirs, @prepended_include_directories);
1189
1190  unshift @{$parser_file_options->{'include_directories'}},
1191          @prepended_include_directories;
1192
1193  my $parser = Texinfo::Parser::parser($parser_file_options);
1194  my $tree = $parser->parse_texi_file($input_file_name);
1195
1196  if (defined($tree)
1197      and (defined(get_conf('DUMP_TREE'))
1198           or (get_conf('DEBUG') and get_conf('DEBUG') >= 10))) {
1199    # this is very wrong, but a way to avoid a spurious warning.
1200    no warnings 'once';
1201    local $Data::Dumper::Purity = 1;
1202    no warnings 'once';
1203    local $Data::Dumper::Indent = 1;
1204    print STDERR Data::Dumper->Dump([$tree]);
1205  }
1206  if (!defined($tree) or $format eq 'parse') {
1207    handle_errors($parser, $error_count, \@opened_files);
1208    next;
1209  }
1210
1211
1212  if ($tree_transformations{'fill_gaps_in_sectioning'}) {
1213    my ($filled_contents, $added_sections)
1214      = Texinfo::Transformations::fill_gaps_in_sectioning($tree);
1215    if (!defined($filled_contents)) {
1216      document_warn(__("fill_gaps_in_sectioning transformation return no result. No section?"));
1217    } else {
1218      $tree->{'contents'} = $filled_contents;
1219    }
1220  }
1221  if ((get_conf('SIMPLE_MENU')
1222       and $formats_table{$format}->{'simple_menu'})
1223      or $tree_transformations{'simple_menus'}) {
1224    $parser->Texinfo::Transformations::set_menus_to_simple_menu();
1225  }
1226
1227  if (defined(get_conf('MACRO_EXPAND')) and $file_number == 0) {
1228    require Texinfo::Convert::Texinfo;
1229    my $texinfo_text = Texinfo::Convert::Texinfo::convert($tree, 1);
1230    #print STDERR "$texinfo_text\n";
1231    my $macro_expand_file = get_conf('MACRO_EXPAND');
1232    my $macro_expand_fh = Texinfo::Common::open_out($parser, $macro_expand_file);
1233
1234    my $error_macro_expand_file;
1235    if (defined($macro_expand_fh)) {
1236      print $macro_expand_fh $texinfo_text;
1237      if (!close($macro_expand_fh)) {
1238        document_warn(sprintf(__("error on closing macro expand file %s: %s\n"),
1239                              $macro_expand_file, $!));
1240        $error_macro_expand_file = 1;
1241      }
1242      $parser->Texinfo::Convert::Converter::register_close_file($macro_expand_file);
1243    } else {
1244      document_warn(sprintf(__("could not open %s for writing: %s\n"),
1245                            $macro_expand_file, $!));
1246      $error_macro_expand_file = 1;
1247    }
1248
1249    if ($error_macro_expand_file) {
1250      $error_count++;
1251      _exit($error_count, \@opened_files);
1252    }
1253  }
1254  if (get_conf('DUMP_TEXI') or $formats_table{$format}->{'texi2dvi_format'}) {
1255    handle_errors($parser, $error_count, \@opened_files);
1256    next;
1257  }
1258
1259  if ($formats_table{$format}->{'move_index_entries_after_items'}
1260      or $tree_transformations{'move_index_entries_after_items'}) {
1261    Texinfo::Common::move_index_entries_after_items_in_tree($tree);
1262  }
1263
1264  if ($formats_table{$format}->{'relate_index_entries_to_table_entries'}
1265      or $tree_transformations{'relate_index_entries_to_table_entries'}) {
1266    Texinfo::Common::relate_index_entries_to_table_entries_in_tree($tree);
1267  }
1268
1269  if ($tree_transformations{'insert_nodes_for_sectioning_commands'}) {
1270    my ($modified_contents, $added_nodes)
1271     = Texinfo::Transformations::insert_nodes_for_sectioning_commands($parser, $tree);
1272    if (!defined($modified_contents)) {
1273      document_warn(__(
1274       "insert_nodes_for_sectioning_commands transformation return no result. No section?"));
1275    } else {
1276      $tree->{'contents'} = $modified_contents;
1277    }
1278  }
1279
1280  Texinfo::Structuring::associate_internal_references($parser);
1281  # every format needs the sectioning structure
1282
1283  my $structure = Texinfo::Structuring::sectioning_structure($parser, $tree);
1284
1285  if ($structure
1286      and !$formats_table{$format}->{'no_warn_non_empty_parts'}) {
1287    Texinfo::Structuring::warn_non_empty_parts($parser);
1288  }
1289
1290  if ($tree_transformations{'complete_tree_nodes_menus'}) {
1291    Texinfo::Transformations::complete_tree_nodes_menus($parser, $tree);
1292  } elsif ($tree_transformations{'complete_tree_nodes_missing_menu'}) {
1293    Texinfo::Transformations::complete_tree_nodes_missing_menu($parser, $tree);
1294  }
1295
1296  if ($tree_transformations{'regenerate_master_menu'}) {
1297    Texinfo::Transformations::regenerate_master_menu($parser);
1298  }
1299
1300  # this can be done for every format, since information is already gathered
1301  my $floats = $parser->floats_information();
1302
1303  my $top_node;
1304  if ($formats_table{$format}->{'nodes_tree'}) {
1305
1306    # it is not get_conf('FORMAT_MENU') but $parser_options as
1307    # $parser_options is set to the output default and then replaced
1308    # with get_conf('FORMAT_MENU') is needed
1309    if ($parser_options->{'FORMAT_MENU'} eq 'menu') {
1310      Texinfo::Structuring::set_menus_node_directions($parser);
1311    }
1312    $top_node = Texinfo::Structuring::nodes_tree($parser);
1313    if ($parser_options->{'FORMAT_MENU'} eq 'menu') {
1314      Texinfo::Structuring::complete_node_tree_with_menus($parser, $top_node);
1315    }
1316  }
1317  if ($formats_table{$format}->{'floats'}) {
1318    Texinfo::Structuring::number_floats($floats);
1319  }
1320
1321  $error_count = handle_errors($parser, $error_count, \@opened_files);
1322
1323  if ($format eq 'structure') {
1324    next;
1325  }
1326
1327  if ($file_number != 0) {
1328    delete $cmdline_options->{'OUTFILE'} if exists($cmdline_options->{'OUTFILE'});
1329    delete $cmdline_options->{'PREFIX'} if exists($cmdline_options->{'PREFIX'});
1330    delete $cmdline_options->{'SUBDIR'}
1331      if (exists($cmdline_options->{'SUBDIR'}) and get_conf('SPLIT'));
1332  }
1333  my $converter_options = { %$converter_default_options,
1334                            %$cmdline_options,
1335                            %$Texinfo::Config::options };
1336
1337  $converter_options->{'expanded_formats'} = $parser_options->{'expanded_formats'};
1338  $converter_options->{'parser'} = $parser;
1339  $converter_options->{'output_format'} = $format;
1340  $converter_options->{'language_config_dirs'} = \@language_config_dirs;
1341  unshift @{$converter_options->{'include_directories'}},
1342          @prepended_include_directories;
1343
1344  my $converter = &{$formats_table{$format}->{'converter'}}($converter_options);
1345  $converter->output($tree);
1346  push @opened_files, $converter->converter_opened_files();
1347  handle_errors($converter, $error_count, \@opened_files);
1348  my $converter_unclosed_files = $converter->converter_unclosed_files();
1349  if ($converter_unclosed_files) {
1350    foreach my $unclosed_file (keys(%$converter_unclosed_files)) {
1351      if ($unclosed_file eq '-') {
1352        $unclosed_files{$unclosed_file}
1353          = $converter_unclosed_files->{$unclosed_file};
1354      } else {
1355        if (!close($converter_unclosed_files->{$unclosed_file})) {
1356          warn(sprintf(__("%s: error on closing %s: %s\n"),
1357                           $real_command_name, $unclosed_file, $!));
1358          $error_count++;
1359          _exit($error_count, \@opened_files);
1360        }
1361      }
1362    }
1363  }
1364
1365  if (defined(get_conf('INTERNAL_LINKS')) and $file_number == 0
1366      and $formats_table{$format}->{'internal_links'}) {
1367    my $internal_links_text
1368      = $converter->output_internal_links();
1369    # always create a file, even if empty.
1370    $internal_links_text = '' if (!defined($internal_links_text));
1371    my $internal_links_file = get_conf('INTERNAL_LINKS');
1372    my $internal_links_fh = Texinfo::Common::open_out($converter,
1373                                             $internal_links_file);
1374    my $error_internal_links_file;
1375    if (defined ($internal_links_fh)) {
1376      print $internal_links_fh $internal_links_text;
1377
1378      if (!close ($internal_links_fh)) {
1379        warn(sprintf(__("%s: error on closing internal links file %s: %s\n"),
1380                      $real_command_name, $internal_links_file, $!));
1381        $error_internal_links_file = 1;
1382      }
1383      $converter->register_close_file($internal_links_file);
1384    } else {
1385      warn(sprintf(__("%s: could not open %s for writing: %s\n"),
1386                      $real_command_name, $internal_links_file, $!));
1387      $error_internal_links_file = 1;
1388    }
1389    if ($error_internal_links_file) {
1390      $error_count++;
1391      _exit($error_count, \@opened_files);
1392    }
1393  }
1394  if (defined(get_conf('SORT_ELEMENT_COUNT')) and $file_number == 0) {
1395    my $converter_element_count_file
1396      = Texinfo::Convert::TextContent->converter($converter_options);
1397    my $use_sections = (! $formats_table{$format}->{'nodes_tree'}
1398                        or (defined($converter->get_conf('USE_NODES'))
1399                            and !$converter->get_conf('USE_NODES')));
1400    my ($sorted_name_counts_array, $sort_element_count_text)
1401        = Texinfo::Convert::Converter::sort_element_counts(
1402               $converter_element_count_file, $tree, $use_sections,
1403                             get_conf('SORT_ELEMENT_COUNT_WORDS'));
1404
1405    my $sort_element_count_file = get_conf('SORT_ELEMENT_COUNT');
1406    my $sort_element_count_fh = Texinfo::Common::open_out($converter,
1407                                             $sort_element_count_file);
1408    my $error_sort_element_count_file;
1409    if (defined ($sort_element_count_fh)) {
1410      print $sort_element_count_fh $sort_element_count_text;
1411
1412      if (!close ($sort_element_count_fh)) {
1413        warn(sprintf(__("%s: error on closing internal links file %s: %s\n"),
1414                      $real_command_name, $sort_element_count_file, $!));
1415        $error_sort_element_count_file = 1;
1416      }
1417      $converter->register_close_file($sort_element_count_file);
1418    } else {
1419      warn(sprintf(__("%s: could not open %s for writing: %s\n"),
1420                    $real_command_name, $sort_element_count_file, $!));
1421      $error_sort_element_count_file = 1;
1422    }
1423    if ($error_sort_element_count_file) {
1424      $error_count++;
1425      _exit($error_count, \@opened_files);
1426    }
1427  }
1428}
1429
1430foreach my $unclosed_file (keys(%unclosed_files)) {
1431  if (!close($unclosed_files{$unclosed_file})) {
1432    warn(sprintf(__("%s: error on closing %s: %s\n"),
1433                     $real_command_name, $unclosed_file, $!));
1434    $error_count++;
1435    _exit($error_count, \@opened_files);
1436  }
1437}
1438
1439if ($call_texi2dvi) {
1440  if (get_conf('DEBUG') or get_conf('VERBOSE')) {
1441    print STDERR "EXEC ".join('|', (get_conf('TEXI2DVI'), @texi2dvi_args, @ARGV))
1442       ."\n";
1443  }
1444  exec { get_conf('TEXI2DVI') } (get_conf('TEXI2DVI'), @texi2dvi_args, @ARGV);
1445}
1446
14471;
1448