File Coverage

lib/Text/Frundis/Processing.pm
Criterion Covered Total %
statement 1784 1927 92.5
branch 1054 1306 80.7
condition 249 343 72.5
subroutine 146 149 97.9
pod 0 133 0.0
total 3233 3858 83.8


", ", \n",
line stmt bran cond sub pod time code
1             #!/usr/bin/env perl
2             # Copyright (c) 2014, 2015 Yon
3             #
4             # Permission to use, copy, modify, and distribute this software for any
5             # purpose with or without fee is hereby granted, provided that the above
6             # copyright notice and this permission notice appear in all copies.
7             #
8             # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9             # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10             # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11             # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12             # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13             # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14             # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15             #
16             # Main Processing
17             #
18             package Text::Frundis::Processing;
19              
20 3     3   9 use utf8;
  3         3  
  3         11  
21 3     3   73 use v5.12;
  3         6  
22 3     3   10 use strict;
  3         2  
  3         43  
23 3     3   6 use warnings;
  3         3  
  3         72  
24 3     3   8 use open qw(:std :utf8);
  3         3  
  3         11  
25              
26 3     3   210 use Carp;
  3         4  
  3         127  
27 3     3   1317 use Encode;
  3         23161  
  3         197  
28 3     3   756 use File::Spec::Functions;
  3         1071  
  3         203  
29 3     3   813 use File::Copy;
  3         4974  
  3         116  
30 3     3   10 use File::Basename;
  3         6  
  3         155  
31 3     3   1292 use URI;
  3         9082  
  3         69  
32 3     3   802 use Text::Frundis::Object qw(@Arg);
  3         3  
  3         291  
33 3     3   705 use Text::Frundis::PerlEval;
  3         4  
  3         42881  
34              
35             # Global Constants and Variables [[[
36             our @Arg;
37              
38             my %Opts;
39             my @FrundisINC;
40             my %FileParse;
41              
42             # Regexes
43             my %Rx;
44              
45             # Phase
46             my $Process = 0; # whether in Processing Phase.
47              
48             # State information
49             my %Count; # counters
50             my %Flag; # state flags
51             my %Filters; # filters for "Bf -t"
52             my %Macro; # user defined macros with `.#de' macro
53             my %BfMacro; # "Bf" macro state
54             my %DeMacro; # "#de" macro state
55             my %UserMacroCall; # user macro call state
56             my %Scope; # scope state information
57             my %State; # miscellaneous state information
58              
59             # Permissions
60             my @Phrasing = qw(Bm Em Sm Bf Ef Ft Lk Sx Im);
61             my @ProcessDirectives = ("#fl", "#if", "#;", "#de", "#.", "#dv");
62             my %AllowedInBl = map { $_ => 1 } qw(Bl It El If Ta), @Phrasing,
63             @ProcessDirectives;
64             my %HtmlPhrasing = map { $_ => 1 }
65             qw(a abbr area audio b bdi bdo br button canvas cite code data datalist del
66             dfn em embed i iframe img input ins kbd keygen label link map mark math
67             meta meter noscript object output progress q ruby s samp script select
68             small span strong sub sup svg template textarea time u var video wbr text);
69             my %HtmlContainingFlow = map { $_ => 1 }
70             qw(article blockquote div header figure footer main pre section);
71              
72             # "pre" is an exception in that it can be useful as a "Bd", but can contain
73             # only phrasing elements
74              
75             my %AllowedParam;
76             my %AllowedFlag;
77              
78             # Macro handlers
79             my %BuiltinMacroHandler = (
80             Bd => \&handle_Bd_macro,
81             Bf => \&handle_Bf_macro,
82             Bl => \&handle_Bl_macro,
83             Bd => \&handle_Bd_macro,
84             Bm => \&handle_Bm_macro,
85             Pt => \&handle_header_macro,
86             Ch => \&handle_header_macro,
87             Sh => \&handle_header_macro,
88             Ss => \&handle_header_macro,
89             P => \&handle_P_macro,
90             D => \&handle_P_macro,
91             Ed => \&handle_Ed_macro,
92             Ef => \&handle_Ef_macro,
93             El => \&handle_El_macro,
94             Em => \&handle_Em_macro,
95             Ft => \&handle_Ft_macro,
96             If => \&handle_If_macro,
97             Im => \&handle_Im_macro,
98             It => \&handle_It_macro,
99             Lk => \&handle_Lk_macro,
100             Sm => \&handle_Sm_macro,
101             Sx => \&handle_Sx_macro,
102             Ta => \&handle_Ta_macro,
103             Tc => \&handle_Tc_macro,
104             X => \&handle_X_macro,
105             '#de' => \&handle_de_macro,
106             '#dv' => \&handle_dv_macro,
107             '#fl' => \&handle_fl_macro,
108             '#.' => \&handle_end_macro,
109             '#;' => \&handle_if_end_macro,
110             '#if' => \&handle_if_macro,
111             );
112              
113             my %BlockEnd = (
114             '#de' => '#.',
115             '#if' => '#;',
116             Bd => 'Ed',
117             Bl => 'El',
118             Bm => 'Em',
119             );
120              
121             # Information collecting variables
122             my %loXstack;
123             my %InfosFlag;
124             my %ID; # label/id in Sm and Bd
125             my %Param; # Global Parameters
126             my @Image; # For collecting image names to copy images in epub dir
127             my %Xmtag;
128             my %Xdtag;
129              
130             # Input/output variables
131             my $FH; # global main source filehandle
132             our $File; # current input file
133             my $SourceText; # main source text
134              
135             my %Lang_mini = ( # [[[
136             af => "afrikaans",
137             bg => "bulgarian",
138             br => "breton",
139             ca => "catalan",
140             cs => "czech",
141             cy => "welsh",
142             da => "danish",
143             de => "german",
144             el => "greek",
145             en => "english",
146             eo => "esperanto",
147             es => "spanish",
148             et => "estonian",
149             eu => "basque",
150             fi => "finnish",
151             fr => "french",
152             ga => "irish",
153             gd => "scottish",
154             gl => "galician",
155             he => "hebrew",
156             hr => "croatian",
157             hu => "magyar",
158             ia => "interlingua",
159             is => "icelandic",
160             it => "italian",
161             la => "latin",
162             nl => "dutch",
163             no => "norsk",
164             pl => "polish",
165             pt => "portuges",
166             ro => "romanian",
167             ru => "russian",
168             se => "samin",
169             sk => "slovak",
170             sl => "slovene",
171             sr => "serbian",
172             sv => "swedish",
173             tr => "turkish",
174             uk => "ukrainian",
175             ); # ]]]
176              
177             my %Lang_babel = %Lang_mini;
178             $Lang_babel{de} = "ngerman";
179             $Lang_babel{fr} = "frenchb";
180              
181             # some traductions of "Index"
182             my %IndexTraductions = (
183             de => "Index",
184             en => "Index",
185             eo => "Indekso",
186             es => "Índice",
187             fr => "Index",
188             );
189              
190             # Escapes [[[
191              
192             my %Latex_escapes = (
193             '{' => '\{',
194             '}' => '\}',
195             '[' => '[',
196             ']' => ']',
197             '%' => '\%',
198             '&' => '\&',
199             '$' => '\$',
200             '#' => '\#',
201             '_' => '\_',
202             '^' => '\^{}',
203             "\\" => '\textbackslash{}',
204             '~' => '\~{}',
205             "\x{a0}" => '~',
206             );
207              
208             my %Xhtml_escapes = (
209             '&' => '&',
210             '<' => '<',
211             '>' => '>',
212             '"' => '"',
213             "'" => ''',
214             );
215              
216             my %Frundis_escapes = (
217             '\e' => "\\",
218             '\&' => '',
219             '\~' => "\x{a0}",
220             );
221              
222             # ]]]
223              
224             # Frundis main object (for exposed api, mainly)
225             my $Self;
226              
227             # ]]]
228              
229             # Collecting and Processing [[[
230              
231             sub init_global_variables {
232              
233             diag_fatal("invalid format argument:$Opts{target_format}")
234 60 50   60 0 316 unless $Opts{target_format} =~ /^(?:latex|xhtml|epub)$/;
235              
236 60 100       144 if ($Opts{target_format} eq "xhtml") {
237             $Opts{standalone} = 1
238 30 100       65 unless $Opts{all_in_one_file}; # Always do -s unless -a is specified
239             }
240              
241             %Rx = (
242 60         1181 xhtml_or_epub => qr{\b(?:xhtml|epub)\b},
243             format => qr{\b$Opts{target_format}\b},
244             valid_format => qr{^(?:epub|latex|xhtml)(?:,(?:epub|latex|xhtml))*$},
245             );
246              
247 60         150 %AllowedParam = map { $_ => 1 }
  1200         1484  
248             qw(dmark document-author document-date document-title encoding
249             epub-cover epub-css epub-metadata epub-subject epub-uuid epub-version
250             lang latex-preamble nbsp title-page xhtml-bottom xhtml-css
251             xhtml-index xhtml-top xhtml5);
252 60         153 %AllowedFlag = map { $_ => 1 } qw(ns fr-nbsp-auto);
  120         200  
253              
254             $Self = Text::Frundis::Object->new(
255             {
256             allowed_params => \%AllowedParam,
257             allowed_flags => \%AllowedFlag,
258             ID => \%ID,
259             file => \$File,
260             filters => \%Filters,
261             flags => \%Flag,
262             format => $Opts{target_format}, # it doesn't change
263 60         908 loX => {},
264             loXstack => \%loXstack,
265             macros => \%Macro,
266             params => \%Param,
267             process => \$Process,
268             state => \%State,
269             vars => {},
270             ivars => {},
271             }
272             );
273              
274 60         1429 %FileParse = ();
275              
276 60 50       169 if ($ENV{FRUNDISLIB}) {
277 0 0       0 if ($^O eq "MSWin32") {
278 0         0 @FrundisINC = split /;/, $ENV{FRUNDISLIB};
279             }
280             else {
281 0         0 @FrundisINC = split /:/, $ENV{FRUNDISLIB};
282             }
283             }
284             }
285              
286             sub process_frundis_source {
287 60     60 0 64 my ($opts) = @_;
288 60         320 %Opts = %$opts;
289              
290 60         836 open(my $stdout_copy, '>&', select);
291 60         357 open(my $stderr_copy, '>&', STDERR);
292 60         93 local *STDOUT;
293 60         78 local *STDERR;
294 60 50       278 open(STDOUT, '>&', $stdout_copy) or die diag_fatal("redirecting stdout:$!");
295 60 50       235 open(STDERR, '>&', $stderr_copy) or die diag_fatal("redirecting stderr:$!");
296              
297 60 100       143 if ($Opts{input_file}) {
    50          
298              
299             diag_warning("useless use of 'input_string' parameter")
300 59 50       104 if $Opts{input_string};
301              
302             # read from a file
303 59         84 $File = $Opts{input_file};
304 59 50       1371 open($FH, '< :bytes', $File) or diag_fatal("$File:$!");
305             {
306 59         59 local $/;
  59         160  
307 59         754 $SourceText = <$FH>;
308 59         366 close $FH;
309             }
310             }
311             elsif ($Opts{input_string}) {
312 1         2 $File = "string";
313 1         4 $SourceText = Encode::encode_utf8($Opts{input_string});
314             }
315             else {
316             # read from stdin
317 0         0 $File = "stdin";
318             {
319 0         0 local $/;
  0         0  
320 0         0 binmode STDIN, ":bytes";
321 0         0 $SourceText = ;
322 0         0 binmode STDIN, ":encoding(utf-8)";
323             }
324             }
325              
326 60         131 init_global_variables();
327              
328             # FIRST PASS : Collecting Phase
329 60         119 init_state();
330 60         130 init_infos();
331 60 50   2   484 open($FH, '<', \$SourceText) or diag_fatal($!);
  2         13  
  2         2  
  2         13  
332              
333             # For testing purposes, redirect stderr to output file if requested
334 60 50 66     1767 if (
      66        
335             $Opts{redirect_stderr}
336             and ( $Opts{all_in_one_file} && $Opts{target_format} eq "xhtml"
337             or $Opts{target_format} eq "latex")
338             )
339             {
340 48 50       2314 open(STDERR, '>', $Opts{output_file}) or diag_fatal($!);
341             }
342 60         131 $FileParse{$File} = parse_file($FH);
343 60         110 close $FH;
344 60         129 collect_source_infos($FileParse{$File});
345              
346             # SECOND PASS : Processing Phase
347 60         98 init_state();
348 60 100       225 if ($Opts{target_format} eq "latex") {
    100          
    50          
349 27 50       203 open($FH, '<', \$SourceText) or diag_fatal($!);
350 27 100       57 if (defined $Opts{output_file}) {
351 26         39 redirect_stds();
352             }
353              
354 27 100       58 if ($Opts{standalone}) {
355 2         10 latex_document_begin($FH);
356 2         6 process_whole_source();
357 2         6 latex_document_end();
358             }
359             else {
360 25         40 process_whole_source();
361             }
362             }
363             elsif ($Opts{target_format} eq "xhtml") {
364 30 50       239 open($FH, '<', \$SourceText) or diag_fatal($!);
365 30 100 33     124 if (defined $Opts{output_file} and $Opts{all_in_one_file}) {
    50          
366 27         45 redirect_stds();
367             }
368             elsif (defined $Opts{output_file}) {
369 3 50       35 unless (-d $Opts{output_file}) {
370 3 50       222 mkdir $Opts{output_file} or diag_fatal("$Opts{output_file}:$!");
371             }
372 3 50       177 open(STDOUT, '>', catfile($Opts{output_file}, "index.html"))
373             or diag_fatal("$Opts{output_file}:$!");
374             }
375              
376 30 100       60 if ($Opts{standalone}) {
377 6   50     22 my $title = $Param{'document-title'} // "";
378 6         19 xhtml_document_header($title);
379 6         19 xhtml_titlepage();
380 6 100       13 unless ($Opts{all_in_one_file}) {
381 3 50       8 if ($Param{'xhtml-index'} eq "full") {
    0          
382 3         8 xhtml_toc("xhtml");
383             }
384             elsif ($Param{'xhtml-index'} eq "summary") {
385 0         0 xhtml_toc("xhtml", { summary => 1 });
386             }
387             }
388 6         16 process_whole_source();
389 6 100       15 if ($State{_xhtml_navigation_text}) {
390              
391             # bottom navigation bar in last file
392 3         9 print $State{_xhtml_navigation_text};
393             }
394 6         15 xhtml_document_footer();
395             }
396             else {
397 24         34 process_whole_source();
398             }
399             }
400             elsif ($Opts{target_format} eq "epub") {
401 3 50       46 unless (-d $Opts{output_file}) {
402 3 50       236 mkdir $Opts{output_file} or diag_fatal("$Opts{output_file}:$!");
403             }
404 3         21 my $EPUB = catdir($Opts{output_file}, "EPUB");
405 3 50       41 unless (-d $EPUB) {
406 3 50       106 mkdir $EPUB or diag_fatal("$EPUB:$!");
407             }
408 3         12 my $META_INF = catdir($Opts{output_file}, "META-INF");
409 3 50       34 unless (-d $META_INF) {
410 3 50       114 mkdir $META_INF
411             or diag_fatal("$META_INF:$!");
412             }
413 3         17 epub_gen();
414 3 50       27 open($FH, '<', \$SourceText) or diag_fatal($!);
415 3         16 my $index_xhtml = catfile($EPUB, "index.xhtml");
416 3 50       123 open(STDOUT, '>', $index_xhtml)
417             or diag_fatal("$index_xhtml:$!");
418 3   50     11 my $title = $Param{'document-title'} // "";
419 3         10 xhtml_document_header($title);
420 3         7 xhtml_titlepage();
421 3         7 process_whole_source();
422 3         6 xhtml_document_footer();
423             }
424              
425             }
426              
427             sub redirect_stds { # [[[
428 53 100   53 0 91 my $mode = $Opts{redirect_stderr} ? '>>' : '>';
429             open(STDOUT, $mode, $Opts{output_file})
430 53 50       1514 or diag_fatal("$Opts{output_file}:$!");
431 53 100       115 if ($Opts{redirect_stderr}) {
432 48 50       632 open(STDERR, '>&', STDOUT) or diag_fatal($!);
433             }
434             } # ]]]
435              
436             # ]]]
437              
438             ################################################################################
439             # Main program source process functions
440              
441             sub collect_source_infos { # [[[
442 112     112 0 87 my $parse = shift;
443              
444 112         86 $Process = 0;
445              
446 112         165 BLOCK: foreach my $block (@$parse) {
447              
448 2142 100 100     6331 if ($Scope{de} and not(@$block == 3 and $block->[0] eq "#.")) {
    100 100        
      100        
      100        
449 72 100       103 unless ($DeMacro{ignore}) {
450 59         43 push @{ $Macro{ $DeMacro{name} }{parse} }, $block;
  59         112  
451             }
452 72         88 next BLOCK;
453             }
454             elsif ($Count{if_ignore}
455             and not(@$block == 3 and $block->[0] =~ /^(?:#;|#if)$/))
456             {
457 12         13 next BLOCK;
458             }
459              
460 2058 100       2572 next unless @$block == 3;
461              
462 1635         1467 $State{macro} = $block->[0];
463 1635         1078 $State{lnum} = $block->[2];
464 1635         1024 @Arg = map { interpolate_vars($_) } @{ $block->[1] };
  2806         2494  
  1635         1813  
465              
466 1635         1828 collect_macro_infos();
467             }
468             } # ]]]
469              
470             sub collect_macro_infos { # [[[
471 1636     1636 0 1181 my $macro = $State{macro};
472 1636 100       3233 if ($Macro{$macro}) { handle_user_macro(); }
  90 100       124  
473             elsif (exists $BuiltinMacroHandler{$macro}) {
474 1542         2074 $BuiltinMacroHandler{$macro}->();
475             }
476             } # ]]]
477              
478             sub process_whole_source { # [[[
479 60     60 0 132 process_source($FileParse{$File});
480 60         81 $State{macro} = "End Of File";
481 60         84 close_unclosed_blocks("Bm");
482 60         82 close_unclosed_blocks("Bl");
483 60         82 close_unclosed_blocks("Bd");
484 60         71 test_for_unclosed_block("#if");
485 60         82 test_for_unclosed_format_block();
486 60         86 test_for_unclosed_de();
487 60 100 66     117 $State{wanted_space} = 1 if $State{text} and $State{wants_space};
488 60         80 close_eventual_final_paragraph();
489             diag_warning(
490             "ns flag set to 1 at end of file, perhaps you forgot a '.#fl ns 0'")
491 60 100       2017 if $Flag{ns};
492             } # ]]]
493              
494             sub parse_file { # [[[
495 69     69 0 70 my $fh = shift;
496              
497 69         74 my $text = ""; # to collect consecutives lines of text
498 69         61 my $text_lnum = 0; # text position
499 69         53 my @parse;
500              
501 69         519 LINE: while (<$fh>) {
502 2477         2182 $State{lnum} = $.;
503 2477 100       4436 diag_warning("trailing space") if /\h$/;
504 2477         2154 s/\\".*//; # comments
505 2477 100       3712 next LINE if /^\.\s*$/; # comment line
506              
507 2435 100       3816 if (/^\.\s*(.*)/) {
508 1621         1821 my $macro_line = $1;
509              
510 1621         1261 chomp $macro_line;
511 1621         2348 while ($macro_line =~ m{\\$}) {
512              
513             # prolonged line
514 5         20 $macro_line =~ s/\\$/ /;
515 5         12 $macro_line .= <$fh>;
516 5         13 chomp $macro_line;
517             }
518              
519 1621         1662 my ($macro, $args) = parse_macro_line($macro_line);
520              
521 1621 50       2003 unless (defined $macro) {
522 0         0 diag_error(
523             "a macro line should start by the name of a valid macro");
524 0         0 next LINE;
525             }
526              
527 1621 100       1817 if ($text) {
528 445         528 push @parse, [ $text, $text_lnum ];
529 445         361 $text = "";
530 445         319 $text_lnum = 0;
531             }
532              
533 1621         4745 push @parse, [ $macro, $args, $State{lnum} ];
534             }
535             else {
536 814         816 $text .= $_;
537 814 100       1285 unless ($text_lnum) {
538 457         843 $text_lnum = $State{lnum};
539             }
540             }
541             }
542              
543 69 100       98 if ($text) {
544 12         19 push @parse, [ $text, $text_lnum ];
545             }
546              
547             # A block is [ $text, $lnum ] or [ $macro, $args, $lnum ].
548 69         164 return \@parse;
549             } # ]]]
550              
551             sub process_source { # [[[
552 112     112 0 117 my $parse = shift;
553              
554 112         100 $Process = 1;
555              
556 112         139 BLOCK: foreach my $block (@$parse) {
557              
558 2142 100 100     7383 if ($Scope{de} and not(@$block == 3 and $block->[0] eq "#.")) {
    100 100        
      100        
      100        
559 72 100       108 unless ($DeMacro{ignore}) {
560 59         40 push @{ $Macro{ $DeMacro{name} }{parse} }, $block;
  59         110  
561             }
562 72         135 next BLOCK;
563             }
564             elsif ($Count{if_ignore}
565             and not(@$block == 3 and $block->[0] =~ /^(?:#;|#if)$/))
566             {
567 12         14 next BLOCK;
568             }
569              
570 2058 100       2406 if (@$block == 3) {
571 1635         1658 $State{macro} = $block->[0];
572 1635         1151 $State{lnum} = $block->[2];
573 1635         1110 @Arg = map { interpolate_vars($_) } @{ $block->[1] };
  2806         2526  
  1635         2120  
574 1635 100       2307 $State{wanted_space} = $State{text} ? $State{wants_space} : 0;
575 1635         1707 process_macro();
576             }
577             else {
578 423         456 $State{lnum} = $block->[1];
579 423 100       606 unless ($Flag{_ignore_text}) {
580 411 100       486 if ($Flag{_verbatim}) {
581             $State{text} .=
582 14         28 escape_verbatim(interpolate_vars($block->[0]));
583             }
584             else {
585 397         534 $State{text} .= escape_text(interpolate_vars($block->[0]));
586             }
587             }
588             }
589             }
590              
591 112         137 return;
592             } # ]]]
593              
594             sub process_macro { # [[[
595 1646     1646 0 1197 my $macro = $State{macro};
596 1646 100 100     3491 if ((not $Macro{$macro}) and test_if_not_allowed_macro($macro)) {
597 5         7 return;
598             }
599 1641 100       2883 if ($Macro{$macro}) { handle_user_macro(); }
  90 100       110  
600             elsif (exists $BuiltinMacroHandler{$macro}) {
601 1547         2043 $BuiltinMacroHandler{$macro}->();
602             }
603             else {
604 4         11 diag_error(
605             "undefined macro `.$macro' (at least for '$Opts{target_format}' output format)"
606             );
607             }
608             } # ]]]
609              
610             ################################################################################
611             # Macro specific functions, in alphabetic order (almost).
612              
613             sub handle_Bd_macro { # [[[
614 64     64 0 142 my %opts = parse_options({ t => "s", id => "s" });
615              
616 64   50     186 $opts{id} //= "";
617 64   100     124 $opts{t} //= "";
618              
619 64         80 $opts{id} = escape_text($opts{id});
620 64 100       107 unless ($Process) {
621 30 50       35 $ID{ $opts{id} } = xhtml_gen_href("", $opts{id}) if $opts{id};
622 30         47 return;
623             }
624 34 50       67 if ($opts{id} =~ /\s/) {
625 0         0 diag_error("id identifier should not contain spaces");
626             }
627              
628 34 100       82 if (@Arg) {
629 2         4 diag_error("`.Bd' macro has useless arguments");
630             }
631              
632 34         45 close_unclosed_blocks("Bm");
633 34         40 close_unclosed_blocks("Bl");
634              
635 34 100       66 my $last = $opts{t} ? $Xdtag{ $opts{t} }{cmd} : 0;
636              
637 34 50 66     28 if (@{ $Scope{Bd} } and $Scope{Bd}->[0]->{t} eq "literal") {
  34         87  
638 0         0 diag_error(
639             "display block of type '$Scope{Bd}->[0]->{t}' cannot contain nested blocks"
640             );
641 0         0 return;
642             }
643             else {
644 34         41 close_eventual_final_paragraph($last);
645             }
646              
647 34         63 scope_stack_push("Bd", $opts{t}, $opts{id});
648              
649 34 100       52 if ($opts{t} eq "literal") {
650 8         15 $Flag{_fr_nbsp_auto} = $Flag{'fr-nbsp-auto'};
651 8         7 $Flag{'fr-nbsp-auto'} = 0;
652 8 100       43 if ($Opts{target_format} =~ /$Rx{xhtml_or_epub}/) {
    50          
653 4         10 print enclose_begin("pre", { id => $opts{id} }), "\n";
654             }
655             elsif ($Opts{target_format} eq "latex") {
656 4         11 print enclose_begin("verbatim", { env => 1, id => $opts{id} }),
657             "\n";
658 4         8 $Flag{_verbatim} = 1;
659             }
660             }
661             else {
662 26 100       114 if ($opts{t}) {
    100          
663             diag_error("`.Bd' invocation: unknown tag")
664 8 50       16 unless defined $Xdtag{ $opts{t} };
665 8         9 my $cmd = $Xdtag{ $opts{t} }{cmd};
666 8 100       16 if ($cmd) {
    100          
667             print enclose_begin(
668             $cmd,
669             { class => $opts{t}, env => 1, id => $opts{id} }
670 6         18 ),
671             "\n";
672             }
673             elsif ($Opts{target_format} =~ /$Rx{xhtml_or_epub}/) {
674             print enclose_begin(
675             "div",
676             { class => $opts{t}, id => $opts{id} }
677 1         3 ),
678             "\n";
679             }
680             }
681             elsif ($Opts{target_format} =~ /$Rx{xhtml_or_epub}/) {
682 11         32 print enclose_begin("div", { id => $opts{id} }), "\n";
683             }
684             }
685 34 50       69 if ($opts{id}) {
686 0 0       0 print "\\hypertarget{$opts{id}}{}\n" if $Opts{target_format} eq "latex";
687             }
688              
689 34         30 $State{wants_space} = 0;
690 34         125 $Scope{paragraph} = 0;
691             } # ]]]
692              
693             sub handle_Bf_macro { # [[[
694 50 100   50 0 98 return unless $Process;
695              
696 25         106 my %opts = parse_options(
697             {
698             f => "s",
699             ns => "b",
700             filter => "s",
701             t => "s",
702             }
703             );
704 25   100     88 $Scope{format} = $opts{f} // "";
705 25         30 $BfMacro{begin_lnum} = $State{lnum};
706             $BfMacro{begin_file} =
707 25 100       49 $UserMacroCall{depth} > 0 ? $UserMacroCall{file} : $File;
708 25 100       48 $BfMacro{in_macro} = $UserMacroCall{depth} > 0 ? 1 : 0;
709 25         21 $Flag{_verbatim} = 1;
710 25 100       42 if (defined $opts{filter}) {
711 2         10 $opts{filter} = escape_verbatim($opts{filter});
712             }
713 25         25 $BfMacro{filter} = $opts{filter};
714 25         26 $BfMacro{filter_tag} = $opts{t};
715              
716 25 100 66     48 unless (defined $opts{f} or $opts{t}) {
717 2         4 diag_error(
718             "`.Bf' macro:you should specify a -f option or -t option at least");
719 2         2 $Flag{_ignore_text} = 1;
720 2         5 return;
721             }
722 23 100       36 if ($opts{t}) {
723 2 50       4 unless (defined $Filters{ $opts{t} }) {
724 0         0 diag_error("undefined filter tag '$opts{t}' in `.Bf' invocation");
725 0         0 $Flag{_ignore_text} = 1;
726 0         0 return;
727             }
728 2 50       5 if (defined $BfMacro{filter}) {
729 0         0 diag_error("-t and -filter should not be used simultaneously");
730             }
731 2         3 $BfMacro{filter} = $Filters{ $opts{t} }{shell};
732             }
733              
734 23 100 100     227 if (defined $opts{f} and $opts{f} !~ /$Rx{format}/) {
    100          
735 10         18 $Flag{_ignore_text} = 1;
736             }
737             elsif ($State{text}) {
738 5         14 phrasing_macro_begin($opts{ns});
739             }
740              
741 23         49 $State{wants_space} = 0;
742             } # ]]]
743              
744             sub handle_Bl_macro { # [[[
745 152 100   152 0 183 if ($Process) {
746 76         104 handle_Bl_macro_process();
747             }
748             else {
749 76         106 handle_Bl_macro_infos();
750             }
751             } # ]]]
752              
753             sub handle_Bl_macro_infos { # [[[
754 76     76 0 194 my %opts = parse_options(
755             {
756             t => "s",
757             columns => "s",
758             }
759             );
760              
761 76 100 100     452 if (defined $opts{t} and $opts{t} eq "verse") {
    100 100        
762 4         4 $InfosFlag{use_verse} = 1;
763 4         8 my $title = escape_text(args_to_text(\@Arg));
764 4 50       7 return unless $title;
765 4         9 $Count{poem}++;
766             loX_entry_infos(
767             {
768             title => $title,
769             count => $Count{poem},
770 4         14 class => "lop",
771             href_prefix => "poem",
772             }
773             );
774             }
775             elsif (defined $opts{t} and $opts{t} eq "table") {
776              
777             # Self->{lot}
778 34         62 my $title = escape_text(args_to_text(\@Arg));
779 34 100       75 return unless $title;
780 26         23 $Count{table}++;
781             loX_entry_infos(
782             {
783             title => $title,
784             count => $Count{table},
785 26         100 class => "lot",
786             href_prefix => "tbl",
787             }
788             );
789             }
790             } # ]]]
791              
792             sub handle_Bl_macro_process { # [[[
793 76 50   76 0 107 return unless $Process;
794 76         94 close_unclosed_blocks("Bm");
795              
796 76         243 my %opts = parse_options(
797             {
798             t => "s",
799             columns => "s",
800             }
801             );
802              
803 76   100     201 $opts{t} //= "item";
804              
805 76 50       265 unless ($opts{t} =~ /^(?:item|enum|desc|verse|table)$/) {
806 0         0 diag_error("invalid `-t' argument to `.Bl' macro: $opts{t}");
807 0         0 return;
808             }
809              
810 76 100       69 if (@{ $Scope{Bl} }) {
  76         117  
811 6 50       28 if ($Scope{Bl}->[0]->{t} !~ /^(?:item|enum)$/) {
812 0         0 diag_error(
813             "`.Bl' macro of type '$Scope{Bl}->[0]->{t}' cannot be nested");
814 0         0 return;
815             }
816 6 100       11 if ($State{text}) {
817 2         4 give_wanted_space();
818 2         3 flush_normal_text();
819             }
820             }
821             else {
822 70         99 close_eventual_final_paragraph(1);
823             }
824              
825 76         126 scope_stack_push("Bl", $opts{t});
826              
827 76 100       263 if ($opts{t} eq "verse") {
    100          
    100          
    100          
    50          
828 4         6 handle_Bl_verse_macro_process();
829             }
830             elsif ($opts{t} eq "desc") {
831 6         14 print enclose_begin($Param{_list_desc}, { env => 1 }), "\n";
832             }
833             elsif ($opts{t} eq "item") {
834 26         47 print enclose_begin($Param{_list_item}, { env => 1 }), "\n";
835             }
836             elsif ($opts{t} eq "enum") {
837 6         11 print enclose_begin($Param{_list_enum}, { env => 1 }), "\n";
838             }
839             elsif ($opts{t} eq "table") {
840 34         56 handle_Bl_table_macro_process($opts{columns});
841             }
842              
843 76         87 $State{wants_space} = 0;
844 76         143 $Scope{item} = 0;
845             } # ]]]
846              
847             sub handle_Bl_table_macro_process { # [[[
848 34     34 0 36 my $columns = shift;
849 34 100       55 if (@Arg) {
850 26         25 $Count{table}++;
851 26         43 $State{_table_title} = escape_text(args_to_text(\@Arg));
852 26 100       49 if ($Opts{target_format} eq "latex") {
853 8         13 print "\\begin{table}[htbp]\n";
854             }
855             else {
856 18         58 print qq{
\n};
857             }
858             }
859 34         76 print enclose_begin($Param{_list_table}, { env => 1 });
860 34 100       79 if ($Opts{target_format} eq "latex") {
861 12 100       19 unless (defined $columns) {
862 1         3 diag_error("-columns option is required for LaTeX");
863 1         2 $columns = "2";
864             }
865 12 100       43 if ($columns =~ /^\d+$/) {
866 8         29 print "{", "l" x $columns, "}";
867             }
868             else {
869 4         7 print "{", $columns, "}";
870             }
871             }
872 34         37 print "\n";
873 34         48 $State{under_table_scope} = 1;
874             } # ]]]
875              
876             sub handle_Bl_verse_macro_process { # [[[
877 4     4 0 4 my $title;
878 4 50       7 if (@Arg) {
879 4         7 $title = escape_text(args_to_text(\@Arg));
880             }
881 4 100       20 if ($Opts{target_format} =~ /$Rx{xhtml_or_epub}/) {
882 2         5 print qq{
\n};
883             }
884 4 50       8 if (defined $title) {
885 4         4 $Count{poem}++;
886             print enclose_begin(
887             $Param{_poemtitle},
888 4         13 { id => "poem$Count{poem}" }
889             );
890 4         5 print $title;
891 4         7 print enclose_end($Param{_poemtitle}), "\n";
892 4 100       9 print "\\label{poem:$Count{poem}}\n" if $Opts{target_format} eq "latex";
893             }
894 4 100       7 if ($Opts{target_format} eq "latex") {
895 2         5 print enclose_begin($Param{_verse}, { env => 1 }), "\n";
896             }
897             } # ]]]
898              
899             sub handle_Bm_macro { # [[[
900 76     76 0 184 my %opts = parse_options(
901             {
902             t => "s",
903             ns => "b",
904             id => "s",
905             }
906             );
907 76   100     224 $opts{id} //= "";
908 76         90 $opts{id} = escape_text($opts{id});
909 76 100       103 unless ($Process) {
910 34 100       54 $ID{ $opts{id} } = xhtml_gen_href("", $opts{id}) if $opts{id};
911 34         44 return;
912             }
913 42 50       68 if ($opts{id} =~ /\s/) {
914 0         0 diag_error("id identifier should not contain spaces");
915             }
916              
917 42         81 phrasing_macro_begin($opts{ns});
918 42         52 $State{wants_space} = 0;
919              
920 42 50 66     99 if (defined $opts{t} and not defined $Xmtag{ $opts{t} }) {
921 0         0 diag_error("in `.Bm' macro invalid tag argument to `-t' option");
922 0         0 $opts{t} = undef;
923             }
924              
925 42         127 scope_stack_push("Bm", $opts{t}, $opts{id});
926              
927 42         44 my $begin;
928 42 100       60 if (defined $opts{t}) {
929              
930             $begin = enclose_begin(
931             $Xmtag{ $opts{t} }{cmd},
932             { class => $opts{t}, id => $opts{id} }
933 10         32 );
934 10 100       26 if (defined $Xmtag{ $opts{t} }{begin}) {
935 2         3 $begin .= $Xmtag{ $opts{t} }{begin};
936             }
937             }
938 42   66     119 $begin //= enclose_begin($Xmtag{_default}{cmd}, { id => $opts{id} });
939 42 100       77 if ($opts{id}) {
940 2 100       6 if ($Opts{target_format} eq "latex") {
941 1         3 $begin = "\\hypertarget{$opts{id}}{" . $begin;
942             }
943             }
944 42         44 print $begin;
945              
946 42 100       100 if (@Arg) {
947 10 100       13 if (!$State{inline}) {
948 2         5 diag_error("useless arguments to `.Bm' macro");
949             }
950             else {
951 8         14 print escape_text(args_to_text(\@Arg));
952             }
953             }
954             } # ]]]
955              
956             sub handle_Ed_macro { # [[[
957 64 100   64 0 97 return unless $Process;
958 36 100       26 unless (@{ $Scope{Bd} }) {
  36         66  
959 2         4 diag_error("unexpected `.Ed' macro without corresponding `.Bd'");
960 2         5 return;
961             }
962 34         29 my $st = pop @{ $Scope{Bd} };
  34         53  
963              
964 34 100       51 if ($st->{t} eq "literal") {
965 8 100       13 if ($State{text}) {
966 6         11 print $State{text};
967 6         6 $State{text} = "";
968             }
969 8 100       47 if ($Opts{target_format} =~ /$Rx{xhtml_or_epub}/) {
    50          
970 4         6 print enclose_end("pre"), "\n";
971             }
972             elsif ($Opts{target_format} eq "latex") {
973 4         10 print enclose_end("verbatim", { env => 1 }), "\n";
974 4         9 $Flag{_verbatim} = 0;
975             }
976 8   50     15 $Flag{'fr-nbsp-auto'} = $Flag{_fr_nbsp_auto} // 1;
977             }
978             else {
979 26         36 close_eventual_final_paragraph(1);
980              
981 26 100       120 if ($st->{t}) {
    100          
    50          
982 8         11 my $cmd = $Xdtag{ $st->{t} }{cmd};
983 8 100       18 if ($cmd) {
    100          
    50          
984 6         12 print enclose_end($cmd, { env => 1 }), "\n";
985             }
986             elsif ($Opts{target_format} =~ /$Rx{xhtml_or_epub}/) {
987 1         2 print enclose_end("div"), "\n";
988             }
989             elsif ($Opts{target_format} eq "latex") {
990 1         1 print "\n";
991             }
992             }
993             elsif ($Opts{target_format} =~ /$Rx{xhtml_or_epub}/) {
994 11         18 print enclose_end("div"), "\n";
995             }
996             elsif ($Opts{target_format} eq "latex") {
997 7         12 print "\n";
998             }
999             }
1000 34         81 $State{wants_space} = 0;
1001             } # ]]]
1002              
1003             sub handle_Ef_macro { # [[[
1004 58 100   58 0 98 return unless $Process;
1005 29 100       48 unless (defined $Scope{format}) {
1006 2         5 diag_error("unexpected `.Ef' without corresponding `.Bf' invocation");
1007 2         3 return;
1008             }
1009 27 100 100     164 if (!$Scope{format} or $Scope{format} =~ /$Rx{format}/) {
1010 17 100 66     55 if ($BfMacro{filter}) {
    100          
1011 1         5 print_filter($BfMacro{filter}, $State{text});
1012             }
1013             elsif ($BfMacro{filter_tag}
1014             and defined $Filters{ $BfMacro{filter_tag} }{code})
1015             {
1016 2         3 $Flag{_perl} = 1;
1017 2         22 $Filters{ $BfMacro{filter_tag} }{code}->($Self);
1018 2         6 $Flag{_perl} = 0;
1019             }
1020             else {
1021 14         24 print $State{text};
1022             }
1023 17         193 $State{text} = "";
1024             }
1025              
1026 27         30 $State{wants_space} = 0;
1027 27         31 $Scope{format} = "";
1028 27         26 $Flag{_verbatim} = 0;
1029 27         45 $Flag{_ignore_text} = 0;
1030             } # ]]]
1031              
1032             sub handle_El_macro { # [[[
1033 156 100   156 0 259 return unless $Process;
1034 78 100       59 unless (@{ $Scope{Bl} }) {
  78         131  
1035 2         3 diag_error("unexpected `.El' macro without corresponding `.Bl'");
1036 2         4 return;
1037             }
1038 76         55 my $st = pop @{ $Scope{Bl} };
  76         98  
1039              
1040 76 100       125 unless ($Scope{item}) {
1041 10 50 66     49 if ($st->{t} eq "desc") {
    100          
    50          
1042 0         0 diag_error(
1043             "unexpected `.El' macro without previous `.It' in 'desc' list");
1044 0         0 print $Param{_desc_value_begin};
1045             }
1046             elsif ($st->{t} eq "enum" or $st->{t} eq "item") {
1047 2         5 diag_error("unexpected `.El' macro without previous `.It'");
1048 2         4 print $Param{_item_begin};
1049             }
1050             elsif ($State{text}) {
1051 0         0 diag_error(
1052             "`.El' invocation:unexpected accumulated text outside item scope"
1053             );
1054             }
1055             }
1056              
1057 76 100       559 if ($st->{t} eq "verse") {
    100          
    100          
    100          
    50          
1058 4         5 handle_paragraph_end();
1059 4 100       6 if ($Opts{target_format} eq "latex") {
1060 2         5 print enclose_end($Param{_verse}, { env => 1 }), "\n";
1061             }
1062 4 100       20 print qq{\n} if $Opts{target_format} =~ /$Rx{xhtml_or_epub}/;
1063             }
1064             elsif ($st->{t} eq "desc") {
1065 6         9 chomp $State{text};
1066 6         9 give_wanted_space();
1067 6         10 $State{text} .= $Param{_desc_value_end};
1068 6         10 flush_normal_text();
1069 6         18 print enclose_end($Param{_list_desc}, { env => 1 }), "\n";
1070             }
1071             elsif ($st->{t} eq "enum") {
1072 6         5 chomp $State{text};
1073 6         8 give_wanted_space();
1074 6         6 flush_normal_text();
1075 6         6 print $Param{_item_end};
1076 6         11 print enclose_end($Param{_list_enum}, { env => 1 }), "\n";
1077             }
1078             elsif ($st->{t} eq "item") {
1079 26         27 chomp $State{text};
1080 26         33 give_wanted_space();
1081 26         25 flush_normal_text();
1082 26         27 print $Param{_item_end};
1083 26         49 print enclose_end($Param{_list_item}, { env => 1 }), "\n";
1084             }
1085             elsif ($st->{t} eq "table") {
1086 34         58 handle_El_table_macro();
1087             }
1088             else {
1089 0         0 diag_fatal("internal error:handle_El_macro");
1090             }
1091              
1092 76 100       76 $Scope{item} = @{ $Scope{Bl} } ? 1 : 0;
  76         125  
1093 76         156 $State{wants_space} = 0;
1094             } # ]]]
1095              
1096             sub handle_El_table_macro { # [[[
1097 34     34 0 41 chomp $State{text};
1098 34         44 give_wanted_space();
1099 34         37 flush_normal_text();
1100 34 100       57 if ($Scope{item}) {
1101 26         30 print $Param{_table_cell_end};
1102 26         37 print $Param{_table_row_end};
1103             }
1104 34         83 print enclose_end($Param{_list_table}, { env => 1 }), "\n";
1105 34 100       76 if (defined $State{_table_title}) {
1106 26 100       51 if ($Opts{target_format} eq "latex") {
1107 8         20 print "\\caption\{$State{_table_title}\}\n";
1108 8         17 print "\\label\{tbl:$Count{table}\}\n";
1109 8         10 print "\\end{table}\n";
1110             }
1111             else {
1112 18         38 print qq{

$State{_table_title}

\n};
1113 18         24 print "\n";
1114             }
1115 26         30 $State{_table_title} = undef;
1116             }
1117 34         43 $State{under_table_scope} = 0;
1118             } # ]]]
1119              
1120             sub handle_Em_macro { # [[[
1121 74 100   74 0 112 return unless $Process;
1122 44 100       30 unless (@{ $Scope{Bm} }) {
  44         71  
1123 2         3 diag_error("unexpected `.Em' macro without corresponding `.Bm'");
1124 2         3 return;
1125             }
1126 42         54 phrasing_macro_end();
1127              
1128 42         35 my $st = pop @{ $Scope{Bm} };
  42         50  
1129              
1130 42         39 my $end = "";
1131 42 100       64 if (defined $st->{t}) {
1132 10 100       25 if (defined $Xmtag{ $st->{t} }{end}) {
1133 2         4 $end .= $Xmtag{ $st->{t} }{end};
1134             }
1135 10         18 $end .= enclose_end($Xmtag{ $st->{t} }{cmd});
1136             }
1137 42   66     88 $end ||= enclose_end($Xmtag{_default}{cmd});
1138              
1139 42         41 print $end;
1140 42 100       60 if (@Arg) {
1141 18         17 my $close_delim = shift @Arg;
1142 18         19 print escape_text($close_delim);
1143             }
1144 42 100 100     97 if ($st->{id} and $Opts{target_format} eq "latex") {
1145 1         2 print "}";
1146             }
1147              
1148 42 100       104 if (@Arg) {
1149 8 100       14 if (!$State{inline}) {
1150 2         4 diag_error("useless args in macro `.Em'");
1151             }
1152             else {
1153 6 50       8 my $sep = $Flag{ns} ? "" : " ";
1154 6         10 print $sep, escape_text(args_to_text(\@Arg));
1155             }
1156             }
1157             } # ]]]
1158              
1159             sub handle_Ft_macro { # [[[
1160 46 100   46 0 79 return unless $Process;
1161 23         86 my %opts = parse_options(
1162             {
1163             f => "s",
1164             ns => "b",
1165             filter => "s",
1166             }
1167             );
1168              
1169 23 100       55 unless (defined $opts{f}) {
1170 2         4 diag_error("`.Ft' macro invocation: you should specify a -f option");
1171 2         4 return;
1172             }
1173              
1174 21 100 100     13 if (@{ $Scope{Bl} } and not $Scope{item}) {
  21         56  
1175 2         4 diag_error("`.Ft' macro invocation in `.Bl' list outside `.It' scope");
1176 2         4 return;
1177             }
1178              
1179 19 100       142 if ($opts{f} =~ /$Rx{format}/) {
1180 10 100       20 if ($State{text}) {
1181 6         17 phrasing_macro_begin($opts{ns});
1182             }
1183 10 100       21 if (defined $opts{filter}) {
1184             print_filter(
1185 3         14 escape_verbatim($opts{filter}),
1186             escape_verbatim(args_to_text(\@Arg))
1187             );
1188             }
1189             else {
1190 7         18 print escape_verbatim(args_to_text(\@Arg));
1191             }
1192             }
1193 19         522 $State{wants_space} = 0;
1194             } # ]]]
1195              
1196             sub handle_If_macro { # [[[
1197 46     46 0 181 my %opts = parse_options(
1198             {
1199             f => "s",
1200             'as-is' => "b",
1201             filter => "s",
1202             t => "s",
1203             }
1204             );
1205 46 100 100     149 if (defined $opts{f} and $opts{f} !~ /$Rx{format}/) {
1206 2         5 return;
1207             }
1208 44 100       71 unless (@Arg) {
1209 4 100       10 diag_error("The `.If' macro expects a path argument")
1210             if $Process;
1211 4         7 return;
1212             }
1213              
1214 40 100       55 if ($opts{'as-is'}) {
1215 12 100       26 return unless $Process;
1216 6         15 my $file = escape_verbatim(shift @Arg);
1217 6         9 chomp $State{text};
1218 6 100 66     19 print "\n" if $State{wants_space} and not $Flag{ns}; # XXX
1219 6         12 flush_normal_text();
1220 6 100       15 if (defined $opts{filter}) {
    100          
1221 2         6 my $text = slurp_file($file);
1222 2         7 print_filter(escape_verbatim($opts{filter}), $text);
1223             }
1224             elsif (defined $opts{t}) {
1225 2 50       5 unless (defined $Filters{ $opts{t} }) {
1226 0         0 diag_error("`If' invocation:undefined tag '$opts{t}'");
1227 0         0 return;
1228             }
1229 2         5 $State{text} = slurp_file($file);
1230 2 50       7 if (defined $Filters{ $opts{t} }{code}) {
    0          
1231 2         43 $Filters{ $opts{t} }{code}->($Self);
1232             }
1233             elsif (defined $Filters{ $opts{t} }{shell}) {
1234             print_filter(
1235             escape_verbatim($Filters{ $opts{t} }{shell}),
1236             $State{text}
1237 0         0 );
1238             }
1239 2         6 $State{text} = "";
1240             }
1241             else {
1242 2         4 print_file($file);
1243             }
1244             }
1245             else {
1246 28         43 my $file = escape_verbatim(shift @Arg);
1247 28 100       113 if ($file =~ /::/) {
    50          
1248 4 50       8 if ($file =~ /\./) {
1249 0         0 diag_error(
1250             "`.If' invocation:path specified with :: notation should not contain periods:'$file'"
1251             );
1252 0         0 return;
1253             }
1254 4         34 $file = catfile(split /::/, $file);
1255 4         7 $file .= ".frundis";
1256             }
1257             elsif ($file !~ m{[/\.]}) {
1258 0 0       0 $file .= ".frundis" unless -f $file;
1259             }
1260 28 50       320 unless (-f $file) {
1261 0         0 $file = search_inc_file($file);
1262             }
1263 28 100       73 unless ($FileParse{$file}) {
1264 9 50       199 open(my $fh, '<', $file) or diag_fatal("$file:$!");
1265 9         19 $FileParse{$file} = parse_file($fh);
1266 9         64 close $fh;
1267             }
1268 28         34 local $File = $file;
1269 28 100       37 if ($Process) {
1270 14         34 process_source($FileParse{$File});
1271             }
1272             else {
1273 14         35 collect_source_infos($FileParse{$File});
1274             }
1275             }
1276             } # ]]]
1277              
1278             sub handle_Im_macro { # [[[
1279 54 100   54 0 77 if ($Process) {
1280 27         45 handle_Im_macro_process();
1281             }
1282             else {
1283 27         49 handle_Im_macro_infos();
1284             }
1285             } # ]]]
1286              
1287             sub handle_Im_macro_infos { # [[[
1288 27     27 0 31 $InfosFlag{use_graphicx} = 1;
1289 27         80 my %opts = parse_options(
1290             {
1291             ns => "b",
1292             link => "s",
1293             }
1294             );
1295 27 50       57 if (@Arg) {
1296 27         45 my $image = escape_verbatim($Arg[0]);
1297 27         35 push @Image, $image;
1298             }
1299 27 100       68 if (@Arg >= 2) {
1300 8         10 my $caption = escape_text($Arg[1]);
1301 8         9 $Count{fig}++;
1302             loX_entry_infos(
1303             {
1304             title => $caption,
1305             count => $Count{fig},
1306 8         29 class => "lof",
1307             href_prefix => "fig",
1308             }
1309             );
1310             }
1311             } # ]]]
1312              
1313             sub handle_Im_macro_process { # [[[
1314 27 100   27 0 65 my $close_delim = @Arg > 1 ? get_close_delim() : "";
1315 27         77 my %opts = parse_options(
1316             {
1317             ns => "b",
1318             link => "s",
1319             }
1320             );
1321 27 100       66 if (@Arg == 1) {
    50          
1322 19         45 handle_Im_inline_macro_process($close_delim, %opts);
1323             }
1324             elsif (@Arg >= 2) {
1325 8         14 handle_Im_figure_macro_process(%opts);
1326             }
1327             } # ]]]
1328              
1329             sub handle_Im_figure_macro_process { # [[[
1330 8     8 0 8 my %opts = @_;
1331 8         9 $Count{fig}++;
1332 8         8 my $image = $Arg[0];
1333 8         13 my $label = escape_text($Arg[1]);
1334 8 100       22 if (@Arg > 2) {
1335 2         3 diag_error("too many arguments in `.Im' macro");
1336             }
1337 8 100 66     36 if ($image =~ /[{}]/ or $label =~ /[{}]/) {
1338 2         5 diag_error(
1339             q{in `.Im' macro, path argument and label should not contain the characters `{', or `}'}
1340             );
1341 2         5 return;
1342             }
1343 6         9 close_unclosed_blocks("Bm");
1344 6         8 close_unclosed_blocks("Bl");
1345              
1346 6         9 close_eventual_final_paragraph();
1347              
1348 6 100       30 if ($Opts{target_format} eq "latex") {
    50          
1349 3         5 $image = escape_verbatim($image);
1350 3         5 $image = escape_latex_percent($image);
1351 3         4 print "\\begin{center}\n";
1352 3         4 print "\\begin{figure}[htbp]\n";
1353 3         6 print "\\includegraphics{$image}\n";
1354 3         5 print "\\caption{$label}\n";
1355 3         6 print "\\label\{fig:$Count{fig}\}\n";
1356 3         3 print "\\end{figure}\n";
1357 3         7 print "\\end{center}\n";
1358             }
1359             elsif ($Opts{target_format} =~ /$Rx{xhtml_or_epub}/) {
1360 3         9 print qq{
\n};
1361 3 50       6 if ($Opts{target_format} eq "epub") {
1362 0         0 $image =~ s|.*/||;
1363 0         0 $image = escape($image);
1364 0         0 my $u = URI->new($image);
1365 0         0 $u = escape_xhtml_text($u);
1366 0         0 $image = escape_xhtml_text($image);
1367 0         0 my $path = catfile('images', $u);
1368 0         0 print qq| $image\n|;
1369             }
1370             else {
1371 3         5 $image = escape($image);
1372 3         10 my $u = URI->new($image);
1373 3         118 $u = escape_xhtml_text($u);
1374 3         8 $image = escape_xhtml_text($image);
1375 3 50       33 if (defined $opts{link}) {
1376 0         0 my $link = URI->new(escape($opts{link}));
1377 0         0 $link = escape_xhtml_text($link);
1378 0         0 print qq| $image\n|;
1379             }
1380             else {
1381 3         8 print qq| $image\n|;
1382             }
1383             }
1384 3         19 print qq|

$label

\n|;
1385 3         10 print "\n";
1386             }
1387              
1388             } # ]]]
1389              
1390             sub handle_Im_inline_macro_process { # [[[
1391 19     19 0 26 my ($close_delim, %opts) = @_;
1392              
1393 19         23 my $image = $Arg[0];
1394 19 100       59 if ($image =~ /[\{\}]/) {
1395 2         3 diag_error(
1396             q{in `.Im' macro, path argument should not contain the characters `{', or `}'}
1397             );
1398 2         5 return;
1399             }
1400 17         50 phrasing_macro_begin($opts{ns});
1401 17 100       81 if ($Opts{target_format} eq "latex") {
    100          
    50          
1402 6         14 $image = escape_latex_percent(escape_verbatim($image));
1403 6         30 print "\\includegraphics{$image}$close_delim";
1404             }
1405             elsif ($Opts{target_format} eq "epub") {
1406 2         10 $image =~ s|.*/||;
1407 2         7 $image = escape($image);
1408 2         17 my $u = URI->new($image);
1409 2         3699 $u = escape_xhtml_text($u);
1410 2         4 $image = escape_xhtml_text($image);
1411 2         7 my $path = catfile('images', $u);
1412 2         30 print qq|$image$close_delim|;
1413             }
1414             elsif ($Opts{target_format} eq "xhtml") {
1415 9         15 $image = escape($image);
1416 9         51 my $u = URI->new($image);
1417 9         386 $u = escape_xhtml_text($u);
1418 9         15 $image = escape_xhtml_text($image);
1419 9 100       21 if (defined $opts{link}) {
1420 1         3 my $link = URI->new(escape($opts{link}));
1421 1         40 $link = escape_xhtml_text($link);
1422 1         3 print
1423             qq|$image$close_delim|;
1424             }
1425             else {
1426 8         21 print qq|$image$close_delim|;
1427             }
1428             }
1429             } # ]]]
1430              
1431             sub handle_It_macro { # [[[
1432 254 100   254 0 395 return unless $Process;
1433              
1434 127 100       212 unless (@{ $Scope{Bl} }) {
  127         186  
1435 2         5 diag_error("unexpected `.It' macro outside a `.Bl' macro scope");
1436 2         2 return;
1437             }
1438 125         167 close_unclosed_blocks("Bm");
1439              
1440 125         117 my $st = $Scope{Bl}->[0];
1441              
1442 125 100       384 if ($st->{t} eq "desc") {
    100          
    100          
    50          
1443 8         14 handle_It_desc_macro();
1444             }
1445             elsif ($st->{t} =~ /^(?:item|enum)$/) {
1446 60         67 handle_It_itemenum_macro();
1447             }
1448             elsif ($st->{t} eq "table") {
1449 45         60 handle_It_table_macro();
1450             }
1451             elsif ($st->{t} eq "verse") {
1452 12         13 handle_It_verse_macro();
1453             }
1454              
1455 125         127 $State{wants_space} = 0;
1456 125         171 $Scope{item} = 1; # following text belongs to an item
1457             } # ]]]
1458              
1459             sub handle_It_desc_macro { # [[[
1460 8 100   8 0 16 if ($Scope{item}) {
1461 2         3 end_any_previous_item();
1462 2         3 print $Param{_desc_value_end};
1463             }
1464 8 50       15 unless (@Arg) {
1465 0         0 diag_warning("description item of `.It' without name");
1466             }
1467 8         12 my $name = process_inline_macros();
1468             print $Param{_desc_name_begin}, $name,
1469 8         127 $Param{_desc_name_end}, $Param{_desc_value_begin};
1470             } # ]]]
1471              
1472             sub handle_It_itemenum_macro { # [[[
1473 60 100   60 0 84 if ($Scope{item}) {
1474 30         31 end_any_previous_item();
1475 30         31 print $Param{_item_end};
1476             }
1477 60         54 print $Param{_item_begin};
1478 60 100       96 if (@Arg) {
1479 18 50       26 my $space = $Flag{ns} ? "" : "\n";
1480 18         30 print escape_text(args_to_text(\@Arg)), $space;
1481             }
1482             } # ]]]
1483              
1484             sub handle_It_table_macro { # [[[
1485 45 100   45 0 79 if ($Scope{item}) {
1486 19         29 end_any_previous_item();
1487 19         22 print $Param{_table_cell_end};
1488 19         21 print $Param{_table_row_end};
1489             }
1490 45         56 print $Param{_table_row_begin};
1491 45 100       74 unless ($Opts{target_format} eq "latex") {
1492 30         41 print $Param{_table_cell_begin};
1493             }
1494 45 100       61 if (@Arg) {
1495 39 50       60 my $space = $Flag{ns} ? "" : "\n";
1496 39         55 print escape_text(args_to_text(\@Arg)), $space;
1497             }
1498             } # ]]]
1499              
1500             sub handle_It_verse_macro { # [[[
1501 12 100   12 0 20 if (not $Scope{paragraph}) {
    50          
1502 6         7 print $Param{_paragraph_begin};
1503 6         6 $Scope{paragraph} = 1;
1504             }
1505             elsif ($Scope{item}) {
1506 6         9 give_wanted_space();
1507 6         7 flush_normal_text();
1508 6         6 print $Param{_line_break};
1509             }
1510 12 100       20 if (@Arg) {
1511 8         9 print escape_text(args_to_text(\@Arg));
1512             }
1513             } # ]]]
1514              
1515             sub handle_Lk_macro { # [[[
1516 52 100   52 0 88 return unless $Process;
1517 26         32 my $close_delim = get_close_delim();
1518 26         73 my %opts = parse_options(
1519             {
1520             ns => "b",
1521             }
1522             );
1523 26 100       47 unless (@Arg) {
1524 2         4 diag_error("`.Lk' macro requires arguments");
1525 2         4 return;
1526             }
1527              
1528 24         51 phrasing_macro_begin($opts{ns});
1529              
1530 24 50 66     79 if ($Param{lang} eq "fr" and $close_delim =~ /^(?:!|:|\?|;)$/) {
1531 0         0 $close_delim .= $Param{'nbsp'} . $close_delim;
1532             }
1533              
1534 24 100       60 if (@Arg >= 2) {
    50          
1535 6 100       11 if (@Arg > 2) {
1536 2         3 diag_error("too many arguments in `.Lk' macro");
1537             }
1538 6         13 my ($url, $label) = @Arg;
1539 6         9 $url = URI->new(escape($url));
1540 6         6048 $label = escape_text($label);
1541 6 100       37 if ($Opts{target_format} =~ /$Rx{xhtml_or_epub}/) {
    50          
1542 3         5 $url = escape_xhtml_text($url);
1543 3         8 print qq{$label};
1544             }
1545             elsif ($Opts{target_format} eq "latex") {
1546 3         6 $url = escape_latex_percent($url);
1547 3         7 print qq|\\href{$url}{$label}|;
1548             }
1549             }
1550             elsif (@Arg == 1) {
1551 18         21 my $url = shift @Arg;
1552 18         34 my $url_e = URI->new(escape_verbatim($url));
1553             {
1554 18         892 local $Flag{_verbatim} = 1;
  18         32  
1555 18         27 $url = escape_text($url);
1556             }
1557 18 100       120 if ($Opts{target_format} =~ /$Rx{xhtml_or_epub}/) {
    50          
1558 9         13 $url_e = escape_xhtml_text($url_e);
1559 9         18 print qq{$url};
1560             }
1561             elsif ($Opts{target_format} eq "latex") {
1562 9         15 $url_e = escape_latex_percent($url_e);
1563 9         21 print qq|\\url{$url_e}|;
1564             }
1565             }
1566 24         183 print "$close_delim";
1567             } # ]]]
1568              
1569             sub handle_P_macro { # [[[
1570 156 100   156 0 250 return unless $Process;
1571 78 100       186 if ($Scope{paragraph}) {
    100          
    100          
1572 20         33 handle_paragraph_end();
1573             }
1574             elsif ($State{text}) {
1575 37         52 handle_paragraph();
1576             }
1577             elsif ($Opts{target_format} eq "latex") {
1578 8         12 print "\n"; # can be usefull after a display block
1579             }
1580 78         85 $Scope{item} = 0; # for verse
1581              
1582 78 100       188 if ($State{macro} eq "D") {
    100          
1583 4         7 paragraph_begin();
1584 4         5 print escape_text($Param{'dmark'});
1585             }
1586             elsif (@Arg) {
1587 17         27 my $title = process_inline_macros();
1588 17 100       296 if ($Opts{target_format} eq "latex") {
    50          
1589 6         18 print "\\paragraph{$title}\n";
1590             }
1591             elsif ($Opts{target_format} =~ /$Rx{xhtml_or_epub}/) {
1592 11         46 print
1593             qq{

$title\n};

1594             }
1595 17         34 reopen_spanning_blocks();
1596 17         23 $Scope{paragraph} = 1;
1597             }
1598 78         146 $State{wants_space} = 0;
1599             } # ]]]
1600              
1601             sub handle_Sm_macro { # [[[
1602 146     146 0 398 my %opts = parse_options(
1603             {
1604             t => "s",
1605             ns => "b",
1606             id => "s",
1607             }
1608             );
1609 146   100     430 $opts{id} //= "";
1610 146         187 $opts{id} = escape_text($opts{id});
1611 146 100       195 unless ($Process) {
1612 67 100       129 $ID{ $opts{id} } = xhtml_gen_href("", $opts{id}) if $opts{id};
1613 67         119 return;
1614             }
1615 79 50       147 if ($opts{id} =~ /\s/) {
1616 0         0 diag_error("id identifier should not contain spaces");
1617             }
1618              
1619 79 100       129 my $close_delim = @Arg > 1 ? get_close_delim() : "";
1620              
1621 79         71 my $text = "";
1622 79 100 100     198 if (defined $opts{t} and not defined $Xmtag{ $opts{t} }) {
1623 4         5 diag_error(
1624             "`.Sm' macro invocation:invalid tag argument to `-t' option");
1625 4         6 $opts{t} = undef;
1626             }
1627 79 100       100 if (@Arg) {
1628 75         110 $text = escape_text(args_to_text(\@Arg));
1629             }
1630             else {
1631 4         31 diag_error("`.Sm' macro invocation:arguments required");
1632 4         10 return;
1633             }
1634              
1635 75         163 phrasing_macro_begin($opts{ns});
1636              
1637 75         220 my ($begin, $end);
1638 75 100       129 if (defined $opts{t}) {
1639             $begin = enclose_begin(
1640             $Xmtag{ $opts{t} }{cmd},
1641             { class => $opts{t}, id => $opts{id} }
1642 22         73 );
1643 22 100       57 if (defined $Xmtag{ $opts{t} }{begin}) {
1644 4         8 $begin .= $Xmtag{ $opts{t} }{begin};
1645             }
1646 22 100       38 if (defined $Xmtag{ $opts{t} }{end}) {
1647 4         7 $end = $Xmtag{ $opts{t} }{end};
1648             }
1649 22         38 $end .= enclose_end($Xmtag{ $opts{t} }{cmd});
1650             }
1651 75   66     218 $begin //= enclose_begin($Xmtag{_default}{cmd}, { id => $opts{id} });
1652 75   66     175 $end //= enclose_end($Xmtag{_default}{cmd});
1653 75 100       114 if ($opts{id}) {
1654 9 100       26 if ($Opts{target_format} eq "latex") {
1655 2         8 $begin = "\\hypertarget{$opts{id}}{" . $begin;
1656 2         4 $end .= "}";
1657             }
1658             }
1659 75         262 print $begin . $text . $end . $close_delim;
1660             } # ]]]
1661              
1662             sub handle_Sx_macro { # [[[
1663 186 100   186 0 310 return unless $Process;
1664 92         298 my %opts = parse_options(
1665             {
1666             ns => "b",
1667             name => "s",
1668             t => "s",
1669             id => "b",
1670             }
1671             );
1672              
1673 92   100     313 $opts{t} //= "toc";
1674 92 100       150 my $close_delim = @Arg > 1 ? get_close_delim() : "";
1675 92 100       149 unless (@Arg) {
1676 2         4 diag_error("`.Sx' macro invocation:arguments required");
1677 2         4 return;
1678             }
1679 90 50 66     209 unless (defined $Self->{loX}{ $opts{t} } or $opts{id}) {
1680 0         0 diag_error("`.Sx' macro invocation:invalid argument to -type");
1681 0         0 return;
1682             }
1683              
1684 90         136 my $id = args_to_text(\@Arg);
1685 90         120 $id = escape_text($id);
1686 90         87 my $valid_title;
1687             my $loX_entry;
1688 90 100       155 unless ($opts{id}) {
1689 71         57 $valid_title = 1;
1690 71 100       170 unless (exists $Self->{loX}{ $opts{t} }{$id}) {
1691 2         7 diag_error(
1692             "`.Sx' invocation:unknown title for type '$opts{t}':$id");
1693 2         2 $valid_title = 0;
1694             }
1695 71         115 $loX_entry = $Self->{loX}{ $opts{t} }{$id};
1696             }
1697 90         190 phrasing_macro_begin($opts{ns});
1698 90 100       201 my $name = $opts{name} ? escape_text($opts{name}) : process_inline_macros();
1699              
1700 90 100       1322 if ($Opts{target_format} eq "latex") {
    50          
1701 23 100       58 if ($opts{id}) {
    100          
1702 3 50       21 unless ($ID{$id}) {
1703 0         0 diag_error("reference to unknown id '$id'");
1704             }
1705 3         16 print "\\hyperlink{$id}{$name}$close_delim";
1706             }
1707             elsif ($valid_title) {
1708 19         25 my $num = $loX_entry->{count};
1709 19         22 my $prefix = $loX_entry->{href_prefix};
1710 19         95 print "\\hyperref[$prefix:", $num, "]{", $name, "}", $close_delim;
1711             }
1712             else {
1713 1         4 print $name, $close_delim;
1714             }
1715             }
1716             elsif ($Opts{target_format} =~ /$Rx{xhtml_or_epub}/) {
1717 67 100       125 if ($opts{id}) {
    100          
1718 16 50       37 if (not $ID{$id}) {
1719 0         0 diag_error("reference to unknown id '$id'");
1720 0         0 print qq{$name$close_delim};
1721             }
1722             else {
1723 16         81 print qq{$name$close_delim};
1724             }
1725             }
1726             elsif ($valid_title) {
1727 50         59 my $href = $loX_entry->{href};
1728 50         209 print qq{$name$close_delim};
1729             }
1730             else {
1731 1         5 print qq{$name$close_delim};
1732             }
1733             }
1734             } # ]]]
1735              
1736             sub handle_Ta_macro { # [[[
1737 133 100   133 0 198 return unless $Process;
1738 66 100       51 unless (@{ $Scope{Bl} }) {
  66         104  
1739 3         5 diag_error("unexpected `.Ta' macro outside a `.Bl' macro scope");
1740 3         4 return;
1741             }
1742 63 100       85 unless ($State{under_table_scope}) {
1743 2         4 diag_error("found `.Ta' macro in non ``table'' list");
1744 2         3 return;
1745             }
1746 61 100       87 unless ($Scope{item}) {
1747 2         4 diag_error("found `.Ta' macro outside an `.It' scope");
1748 2         3 return;
1749             }
1750 59         72 close_unclosed_blocks("Bm");
1751              
1752 59         55 chomp $State{text};
1753 59         71 give_wanted_space();
1754 59         64 flush_normal_text();
1755 59         58 print $Param{_table_cell_end};
1756 59         57 print $Param{_table_cell_begin};
1757              
1758 59 100       98 if (@Arg) {
1759 51         66 print escape_text(args_to_text(\@Arg)), "\n";
1760             }
1761             } # ]]]
1762              
1763             sub handle_Tc_macro { # [[[
1764 110 100   110 0 131 if ($Process) {
1765 55         86 handle_Tc_macro_process();
1766             }
1767             else {
1768 55         74 handle_Tc_macro_infos();
1769             }
1770             } # ]]]
1771              
1772             sub handle_Tc_macro_infos { # [[[
1773 55     55 0 229 my %opts = parse_options(
1774             {
1775             summary => "b",
1776             nonum => "b",
1777             mini => "b",
1778             toc => "b",
1779             lof => "b",
1780             lot => "b",
1781             title => "s",
1782             }
1783             );
1784 55 100       138 $InfosFlag{use_minitoc} = 1 if $opts{mini};
1785 55 50 66     114 $InfosFlag{dominilof} = 1 if $opts{mini} and $opts{lof};
1786 55 50 66     103 $InfosFlag{dominilot} = 1 if $opts{mini} and $opts{lot};
1787 55 50 66     152 $InfosFlag{dominitoc} = 1 if $opts{mini} and $opts{toc};
1788             } # ]]]
1789              
1790             sub handle_Tc_macro_process { # [[[
1791 55     55 0 84 close_unclosed_blocks("Bm");
1792 55         60 close_unclosed_blocks("Bl");
1793              
1794 55         256 my %opts = parse_options(
1795             {
1796             summary => "b",
1797             nonum => "b",
1798             mini => "b",
1799             toc => "b",
1800             lof => "b",
1801             lot => "b",
1802             title => "s",
1803             }
1804             );
1805              
1806 55         130 close_eventual_final_paragraph();
1807              
1808 55 100 33     201 unless ($opts{toc} or $opts{lof} or $opts{lot}) {
      33        
1809 46         52 $opts{toc} = 1;
1810             }
1811 55 0 66     291 if ( $opts{toc} && $opts{lof}
      66        
      33        
      33        
      33        
1812             or $opts{toc} and $opts{lot}
1813             or $opts{lof} and $opts{lot})
1814             {
1815 0         0 diag_error(
1816             "`.Tc' invocation:only one of the -toc, -lof and -lot options should bet set"
1817             );
1818 0         0 return;
1819             }
1820              
1821 55 100       276 if ($Opts{target_format} eq "latex") {
    50          
1822 15 100       22 if ($opts{summary}) {
1823 3         6 print "\\setcounter{tocdepth}{0}\n";
1824             }
1825             else {
1826 12         23 print "\\setcounter{tocdepth}{3}\n";
1827             }
1828 15 100       21 if ($opts{mini}) {
1829 6 50       18 if ($opts{lof}) {
    50          
1830 0         0 print "\\minilof\n";
1831             }
1832             elsif ($opts{lot}) {
1833 0         0 print "\\minilot\n";
1834             }
1835             else {
1836 6         15 print "\\minitoc\n";
1837             }
1838             }
1839             else {
1840 9 50       24 if ($opts{lof}) {
    100          
1841 0         0 print "\\listoffigures\n";
1842             }
1843             elsif ($opts{lot}) {
1844 2         7 print "\\listoftables\n";
1845             }
1846             else {
1847 7         16 print "\\tableofcontents\n";
1848             }
1849             }
1850             }
1851             elsif ($Opts{target_format} =~ /$Rx{xhtml_or_epub}/) {
1852 40 50       87 if ($opts{lof}) {
    100          
1853 0         0 xhtml_lof(\%opts);
1854             }
1855             elsif ($opts{lot}) {
1856 7         20 xhtml_lot(\%opts);
1857             }
1858             else {
1859 33         62 xhtml_toc("xhtml", \%opts);
1860             }
1861             }
1862             } # ]]]
1863              
1864             sub handle_X_macro { # [[[
1865 404 100   404 0 659 return if $Process;
1866 202 50       270 unless (@Arg) {
1867 0         0 warn diag(
1868             "warning:.$State{macro} invocation: you should specify arguments");
1869 0         0 return;
1870             }
1871              
1872 202         190 my $cmd = shift @Arg;
1873 202 100       482 if ($cmd eq "dtag") {
    100          
    100          
    50          
1874 22         24 handle_X_dtag_macro($cmd);
1875             }
1876             elsif ($cmd eq "ftag") {
1877 3         10 handle_X_ftag_macro($cmd);
1878             }
1879             elsif ($cmd eq "mtag") {
1880 50         61 handle_X_mtag_macro($cmd);
1881             }
1882             elsif ($cmd eq "set") {
1883 127         146 handle_X_set_macro($cmd);
1884             }
1885             } # ]]]
1886              
1887             sub handle_X_dtag_macro { # [[[
1888 22     22 0 15 my $cmd = shift;
1889 22         69 my %opts = parse_options(
1890             {
1891             f => "s",
1892             t => "s",
1893             c => "s",
1894             },
1895             "$State{macro} $cmd",
1896             );
1897 22 100       60 unless (defined $opts{f}) {
1898 2         8 diag_error(
1899             "`.$State{macro} $cmd' invocation: you should specify `-f' option");
1900 2         5 return;
1901             }
1902 20 100       103 unless ($opts{f} =~ /$Rx{valid_format}/) {
1903 2         7 diag_error("`.X $cmd' invocation:invalid argument to -f:$opts{f}");
1904 2         5 return;
1905             }
1906 18 100       85 return unless $opts{f} =~ /$Rx{format}/;
1907 9 100       15 unless (defined $opts{t}) {
1908 1         5 diag_error(
1909             "-t option should have an argument in `.$State{macro} $cmd' invocation"
1910             );
1911 1         3 return;
1912             }
1913              
1914 8         17 $Xdtag{ $opts{t} }{cmd} = $Xdtag{_default}{cmd};
1915 8 100       19 if (defined $opts{c}) {
1916 5 100       15 if (not $opts{c} =~ /^[a-zA-Z]*$/) {
1917 1         6 diag_error(
1918             "`.X $cmd' invocation: invalid argument to -c:$opts{c}:it should be composed of ascii letters"
1919             );
1920             }
1921 5 100       20 if ($Opts{target_format} =~ /$Rx{xhtml_or_epub}/) {
1922             diag_warning(
1923             "`.X $cmd' invocation:possibly inadequate element argument to -c:$opts{c}"
1924             )
1925             unless $opts{c} eq ""
1926 3 100 66     25 or $HtmlContainingFlow{ $opts{c} };
1927             }
1928 5         16 $Xdtag{ $opts{t} }{cmd} = $opts{c};
1929             }
1930             } # ]]]
1931              
1932             sub handle_X_ftag_macro { # [[[
1933 3     3 0 4 my $cmd = shift;
1934 3         18 my %opts = parse_options(
1935             {
1936             f => "s",
1937             t => "s",
1938             shell => "s",
1939             code => "s",
1940             }
1941             );
1942 3 50       11 if (defined $opts{f}) {
1943 0 0       0 unless ($opts{f} =~ /$Rx{valid_format}/) {
1944 0         0 diag_error("`.X $cmd' invocation: invalid argument to -f:$opts{f}");
1945 0         0 return;
1946             }
1947 0 0       0 return unless $opts{f} =~ /$Rx{format}/;
1948             }
1949 3 50       7 unless (defined $opts{t}) {
1950 0         0 diag_error("`.X $cmd' invocation:-t option should be specified");
1951 0         0 return;
1952             }
1953 3 0 33     9 if ($opts{shell} and $opts{code}) {
1954 0         0 diag_error(
1955             "`.X $cmd' invocation:-shell and -code cannot be used simultaneously"
1956             );
1957             }
1958 3         9 $Filters{ $opts{t} }{shell} = $opts{shell};
1959 3 50       19 if ($opts{code}) {
1960             Text::Frundis::PerlEval::_compile_perl_code(
1961             $Self, $opts{t},
1962 3         15 $opts{code}, "filter"
1963             );
1964             }
1965             } # ]]]
1966              
1967             sub handle_X_mtag_macro { # [[[
1968 50     50 0 37 my $cmd = shift;
1969 50         210 my %opts = parse_options(
1970             {
1971             f => "s",
1972             t => "s",
1973             c => "s",
1974             b => "s",
1975             e => "s",
1976             },
1977             "$State{macro} $cmd",
1978             );
1979 50 100       115 unless (defined $opts{f}) {
1980 4         11 diag_error(
1981             "`.$State{macro} $cmd' invocation: you should specify `-f' option");
1982 4         9 return;
1983             }
1984 46 100       275 unless ($opts{f} =~ /$Rx{valid_format}/) {
1985 2         8 diag_error(
1986             "`.X $cmd' invocation:invalid argument to -f option:$opts{f}");
1987 2         5 return;
1988             }
1989 44 100       221 return unless $opts{f} =~ /$Rx{format}/;
1990              
1991 22 100       34 unless (defined $opts{t}) {
1992 3         16 diag_error("`.X $cmd' invocation:-t option should be specified");
1993 3         7 return;
1994             }
1995              
1996 19         52 $Xmtag{ $opts{t} }{cmd} = $Xmtag{_default}{cmd};
1997 19 100 66     87 if (defined $opts{c} and $opts{c} =~ /^[a-zA-Z]*$/) {
1998 13 50       33 if (not $opts{c} =~ /^[a-zA-Z]*$/) {
1999 0         0 diag_error(
2000             "`.X $cmd' invocation: invalid argument to -c:$opts{c}:it should be composed of ascii letters"
2001             );
2002             }
2003 13 100       57 if ($Opts{target_format} =~ /$Rx{xhtml_or_epub}/) {
2004             diag_warning(
2005             "`.X $cmd' invocation:non phrasing element argument to -c:$opts{c}:you should probably use a dtag"
2006             )
2007             unless $opts{c} eq ""
2008 7 100 100     36 or $HtmlPhrasing{ $opts{c} };
2009             }
2010 13         27 $Xmtag{ $opts{t} }{cmd} = $opts{c};
2011             }
2012              
2013             # other optional options
2014 19 100       32 if (defined $opts{b}) {
2015 9         18 $Xmtag{ $opts{t} }{begin} = escape_text($opts{b});
2016             }
2017 19 100       53 if (defined $opts{e}) {
2018 9         12 $Xmtag{ $opts{t} }{end} = escape_text($opts{e});
2019             }
2020              
2021             } # ]]]
2022              
2023             sub handle_X_set_macro { # [[[
2024 127     127 0 93 my $cmd = shift;
2025 127         336 my %opts = parse_options(
2026             {
2027             f => "s",
2028             },
2029             "$State{macro} $cmd",
2030             );
2031 127 100       225 if (defined $opts{f}) {
2032 8 50       57 unless ($opts{f} =~ /$Rx{valid_format}/) {
2033 0         0 diag_error("`.X $cmd' invocation: invalid argument to -f:$opts{f}");
2034 0         0 return;
2035             }
2036 8 100       56 return unless $opts{f} =~ /$Rx{format}/;
2037             }
2038 123 100       174 unless (@Arg >= 2) {
2039 2         7 diag_error("`.X $cmd' invocation expects two arguments");
2040 2         5 return;
2041             }
2042 121 100       155 if (@Arg > 2) {
2043 2         7 diag_error("`.X $cmd' invocation: too many arguments");
2044             }
2045              
2046 121         98 my $parameter = $Arg[0];
2047 121 100       182 unless ($AllowedParam{$parameter}) {
2048 4         12 diag_warning(
2049             "useless `.X set' definition of unknown parameter '$parameter'");
2050             }
2051              
2052 121         190 $Param{$parameter} = $Arg[1];
2053              
2054 121 100 66     581 if ($parameter =~ /^document-(?:author|date|title)$/) {
    100          
    100          
    100          
    100          
2055 25         50 $Param{$parameter} = escape_text($Param{$parameter});
2056             }
2057             elsif ($parameter eq "nbsp") {
2058 1         4 $Xhtml_escapes{'\~'} = $Param{nbsp};
2059             }
2060             elsif ( $parameter eq "xhtml-index"
2061             and $Param{$parameter} !~ /^(?:full|summary|none)$/)
2062             {
2063 2         36 diag_error(
2064             "`.X set' invocation:xhtml-index parameter:unknown value:$Param{$parameter}"
2065             );
2066             }
2067             elsif ($parameter eq "epub-version") {
2068             diag_error(
2069             "`.X set' invocation:epub-version parameter should be 2 or 3")
2070 1 50       6 unless $Param{$parameter} =~ /^(?:2|3)$/;
2071             }
2072             elsif ($parameter eq "lang") {
2073 9 50       33 if ($IndexTraductions{ $Param{lang} }) {
2074 9         28 $Param{_index} = $IndexTraductions{ $Param{lang} };
2075             }
2076             }
2077             } # ]]]
2078              
2079             sub handle_de_macro { # [[[
2080 160 50   160 0 231 if ($Scope{de}) {
2081 0 0       0 diag_error(
2082             "found `.#de' macro in the scope of a previous `.#de' macro at line $DeMacro{lnum}"
2083             ) if $Process;
2084 0         0 return;
2085             }
2086 160         385 my %opts = parse_options(
2087             {
2088             f => "s",
2089             perl => "b",
2090             }
2091             );
2092              
2093 160 50       281 unless (@Arg) {
2094 0 0       0 diag_error("a name should be specified to the `.#de' declaration")
2095             if $Process;
2096 0         0 return;
2097             }
2098 160         151 my $name = shift @Arg;
2099 160 50 33     622 if ($name =~ /^[A-Z][a-z]$/ or $name =~ /^#/) {
2100 0         0 diag_error(
2101             "two letters names of the form Xy and names starting by # are reserved"
2102             );
2103             }
2104 160         128 $Scope{de} = 1;
2105 160         151 $DeMacro{file} = $File;
2106 160         143 $DeMacro{lnum} = $State{lnum};
2107 160         138 $DeMacro{perl} = $opts{perl};
2108 160         139 $DeMacro{name} = $name;
2109 160   100     555 $Macro{ $DeMacro{name} }{parse} //= [];
2110              
2111 160 100       258 if (defined $opts{f}) {
2112 72 50       438 unless ($opts{f} =~ /$Rx{valid_format}/) {
2113 0 0       0 diag_error(
2114             "`.#de' invocation:invalid argument to -f option:$opts{f}")
2115             if $Process;
2116             }
2117 72 100       340 unless ($opts{f} =~ /$Rx{format}/) {
2118 36         43 $DeMacro{ignore} = 1;
2119             }
2120             }
2121              
2122 160 50 33     409 if (@Arg && $Process) {
2123 0         0 diag_error("`.#de' invocation:too many arguments");
2124             }
2125             } # ]]]
2126              
2127             sub handle_dv_macro { # [[[
2128 16     16 0 32 my %opts = parse_options(
2129             {
2130             f => "s",
2131             }
2132             );
2133 16 50       31 unless (@Arg) {
2134 0         0 diag_error("`.dv' requires arguments");
2135 0         0 return;
2136             }
2137 16 50       23 if (defined $opts{f}) {
2138 0 0       0 unless ($opts{f} =~ /$Rx{valid_format}/) {
2139 0         0 diag_error(
2140             "`.dv' invocation:invalid argument to -f option:$opts{f}");
2141 0         0 return;
2142             }
2143 0 0       0 return unless $opts{f} =~ /$Rx{format}/;
2144             }
2145              
2146 16         22 my ($name, @arg) = @Arg;
2147 16 50       22 if (@arg) {
2148 16         34 $Self->{vars}{$name} = join(" ", @arg);
2149 16         26 return;
2150             }
2151             else {
2152 0         0 diag_error("`.dv' invocation:value required");
2153             }
2154             } # ]]]
2155              
2156             sub handle_end_macro { # [[[
2157 160 100   160 0 246 unless ($Scope{de}) {
2158 4 100       7 diag_error("`..' allowed only within a `.#de' macro scope")
2159             if $Process;
2160 4         5 return;
2161             }
2162 156         134 $Scope{de} = 0;
2163 156 100       233 if ($DeMacro{ignore}) {
2164 36         44 reset_de_macro_state();
2165 36         52 return;
2166             }
2167 120 100       203 $Macro{ $DeMacro{name} }{perl} = 1 if $DeMacro{perl};
2168 120 100       157 if ($DeMacro{perl}) {
2169 60         109 my $text = escape_verbatim($Macro{ $DeMacro{name} }{parse}->[0][0]);
2170 60         60 $Flag{_perl} = 1;
2171             Text::Frundis::PerlEval::_compile_perl_code(
2172             $Self, $DeMacro{name},
2173 60         129 $text, "macro"
2174             );
2175 60         92 $Flag{_perl} = 0;
2176             }
2177 120         184 $Macro{ $DeMacro{name} }{lnum} = $DeMacro{lnum};
2178 120         176 reset_de_macro_state();
2179             } # ]]]
2180              
2181             sub handle_fl_macro { # [[[
2182 36 100   36 0 61 return unless $Process;
2183 18 100       29 unless (@Arg) {
2184 2         5 diag_error("`.#fl' requires at least one argument");
2185 2         3 return;
2186             }
2187 16         22 my ($key, $value) = @Arg;
2188 16 100       29 unless ($AllowedFlag{$key}) {
2189 6         13 diag_warning("unsupported key in `.#fl' macro:$key");
2190             }
2191 16 100       26 if (defined $value) {
    50          
2192 12 50 66     48 if (defined $Flag{$key} and $value eq $Flag{$key}) {
2193 0         0 diag_warning("useless use of `.#fl', value doesn't change");
2194 0         0 return;
2195             }
2196 12         26 $Flag{$key} = $value;
2197             }
2198             elsif (defined $Flag{$key}) {
2199 4         11 $Flag{$key} = !$Flag{$key};
2200             }
2201             else {
2202 0         0 diag_warning("use of undefined state value in `.#fl' macro");
2203             }
2204             } # ]]]
2205              
2206             sub handle_header_macro { # [[[
2207 370 100   370 0 418 if ($Process) {
2208 185         228 handle_header_macro_process();
2209             }
2210             else {
2211 185         213 handle_header_macro_infos();
2212             }
2213             } # ]]]
2214              
2215             sub handle_header_macro_infos { # [[[
2216 185     185 0 193 my $macro = $State{macro};
2217 185         397 my %opts = parse_options(
2218             { nonum => "b" },
2219             );
2220 185 100       311 unless (@Arg) {
2221 2         3 return;
2222             }
2223              
2224 183         109 my $href;
2225 183         342 headers_count_update($opts{nonum});
2226 183 100 66     519 if ($macro eq "Pt") {
    100          
    50          
2227 24         23 $InfosFlag{has_part} = 1;
2228 24         44 $href = xhtml_gen_href("s", $Count{header}, 1);
2229             }
2230             elsif ($macro eq "Ch") {
2231 75         78 $InfosFlag{has_chapter} = 1;
2232 75         111 $href = xhtml_gen_href("s", $Count{header}, 1);
2233             }
2234             elsif ($macro eq "Sh" or $macro eq "Ss") {
2235 84 100       110 if ($Opts{all_in_one_file}) {
2236 54         118 $href = xhtml_gen_href("s", "$Count{header}");
2237             }
2238             else {
2239 30         75 $href = xhtml_gen_href("s", "$Count{section}-$Count{subsection}");
2240             }
2241             }
2242 183         162 my $id = $href;
2243 183         477 $id =~ s/.*#//;
2244 183         228 $id =~ s/\.x?html$//;
2245              
2246 183         269 my $title = escape_text(args_to_text(\@Arg));
2247 183 100       406 if (exists $Self->{loX}{toc}{$title}) {
2248 2         7 diag_error(
2249             "The title '$title' is used more than once as header. This will confuse cross-references."
2250             );
2251             }
2252 183         373 my $num = header_number($opts{nonum});
2253             $Self->{loX}{toc}{$title} = {
2254             href => $href,
2255             id => $id,
2256             href_prefix => "s",
2257             num => $num,
2258             count => $Count{header},
2259             nonum => $opts{nonum},
2260 183         764 };
2261              
2262 183 100       426 if ($macro =~ /^(?:Pt|Ch)$/) {
2263 99         302 push @{ $loXstack{nav} },
2264             {
2265             href => $href,
2266             id => $id,
2267             href_prefix => "s",
2268             macro => $macro,
2269             count => $Count{header},
2270 99         73 };
2271             }
2272              
2273 183         955 push @{ $loXstack{toc} },
2274             {
2275             macro => $macro,
2276             id => $id,
2277             href_prefix => "s",
2278             title => $title,
2279             href => $href,
2280             num => $num,
2281             nonum => $opts{nonum},
2282             count => $Count{header},
2283 183         138 };
2284             } # ]]]
2285              
2286             sub handle_header_macro_process { # [[[
2287 185     185 0 138 my $numbered = 1;
2288 185         153 my $title = "";
2289 185 100       274 unless (@Arg) {
2290 2         7 diag_error("`.$State{macro}' macro requires at least one argument");
2291 2         4 return;
2292             }
2293 183         427 my %opts = parse_options(
2294             {
2295             nonum => "b",
2296             },
2297             );
2298 183 100       340 $numbered = 0 if $opts{nonum};
2299 183         266 $title = escape_text(args_to_text(\@Arg));
2300              
2301 183         249 close_unclosed_blocks("Bm");
2302 183         198 close_unclosed_blocks("Bl");
2303              
2304 183         211 close_eventual_final_paragraph();
2305              
2306 183         403 headers_count_update($opts{nonum});
2307 183 100       564 if ($State{macro} =~ /^(?:Pt|Ch)$/) {
2308 99         102 $State{nav_count}++;
2309 99 100 100     519 if ($Opts{target_format} =~ /$Rx{xhtml_or_epub}/
2310             and not $Opts{all_in_one_file})
2311             {
2312 30         58 xhtml_file_output_change($title);
2313             }
2314             }
2315              
2316 183         226 my $toc = $Self->{loX}{toc};
2317              
2318             # opening
2319 183 100 66     700 if ($Opts{target_format} eq "latex") {
    100          
    50          
2320 56         96 my $type = latex_header_name($State{macro});
2321 56 100       78 if ($numbered) {
2322 54         71 print enclose_begin($type);
2323             }
2324             else {
2325 2         7 print enclose_begin($type . "*");
2326             }
2327             }
2328             elsif ($Opts{target_format} eq "xhtml" and $Opts{all_in_one_file}) {
2329             print enclose_begin(
2330             xhtml_section_header($State{macro}),
2331             {
2332             id => "s$toc->{$title}{count}",
2333             class => $State{macro},
2334             }
2335 67         107 );
2336             }
2337             elsif ($Opts{target_format} =~ /$Rx{xhtml_or_epub}/) {
2338 60         96 my $id = $toc->{$title}{id};
2339             print enclose_begin(
2340             xhtml_section_header($State{macro}),
2341             {
2342             id => $id,
2343             class => $State{macro},
2344             }
2345 60         97 );
2346             }
2347              
2348 183         273 my $num = "";
2349 183 100 100     888 if ($Opts{target_format} =~ /$Rx{xhtml_or_epub}/ and not $opts{nonum}) {
2350 120         163 $num = $toc->{$title}{num};
2351 120 50       199 $num = "$num " if $num;
2352             }
2353 183         201 print $num;
2354              
2355 183         304 my $title_render = process_inline_macros();
2356 183         2671 print $title_render;
2357              
2358 183         226 close_unclosed_blocks("Bm");
2359              
2360             # closing
2361 183 100       732 if ($Opts{target_format} eq "latex") {
    50          
2362 56         136 my $type = latex_header_name($State{macro});
2363 56 100       76 if ($numbered) {
2364 54         74 print enclose_end($type), "\n";
2365             }
2366             else {
2367 2         9 print enclose_end($type . "*"), "\n";
2368             print "\\addcontentsline{toc}{"
2369             . latex_header_name($State{macro})
2370 2         5 . "}{$title_render}\n";
2371             }
2372 56         175 print "\\label{s:", $toc->{$title}{count}, "}\n";
2373             }
2374             elsif ($Opts{target_format} =~ /$Rx{xhtml_or_epub}/) {
2375 127         175 print enclose_end(xhtml_section_header($State{macro})), "\n";
2376             }
2377              
2378 183         204 $State{wants_space} = 0;
2379 183         430 $Scope{paragraph} = 0;
2380             } # ]]]
2381              
2382             sub handle_if_macro { # [[[
2383 54     54 0 89 scope_stack_push("#if");
2384 54 50       87 if ($Count{if_ignore}) {
2385 0         0 $Count{if_ignore}++;
2386 0         0 return;
2387             }
2388 54         101 my %opts = parse_options(
2389             {
2390             f => "s",
2391             }
2392             );
2393 54 100 100     162 unless (defined $opts{f} or @Arg) {
2394 8         10 diag_warning("useless `.#if' invocation");
2395 8         11 return;
2396             }
2397              
2398 46 100       83 if (defined $opts{f}) {
2399 26 50       189 unless ($opts{f} =~ /$Rx{valid_format}/) {
2400 0 0       0 diag_error("invalid ``format'' argument in `.#if' macro:$opts{f}")
2401             if $Process;
2402             }
2403 26 100       128 unless ($opts{f} =~ /$Rx{format}/) {
2404 12         15 $Count{if_ignore} = 1;
2405 12         24 return;
2406             }
2407             }
2408              
2409 34 100       61 if (@Arg) {
2410 24         20 my $bool = shift @Arg;
2411 24 50       30 if (@Arg) {
2412 0 0       0 diag_error("`.#if' invocation:too many arguments")
2413             if $Process;
2414             }
2415 24 100       43 unless ($bool) {
2416 6         9 $Count{if_ignore} = 1;
2417             }
2418             }
2419             } # ]]]
2420              
2421             sub handle_if_end_macro { # [[[
2422 50 100   50 0 81 $Count{if_ignore}-- if $Count{if_ignore};
2423 50 100       37 if (@{ $Scope{'#if'} }) {
  50         69  
2424 46         29 pop @{ $Scope{'#if'} };
  46         97  
2425             }
2426             else {
2427 4 100       9 diag_error("`.#;' invocation with no previous `.#if'")
2428             if $Process;
2429             }
2430             } # ]]]
2431              
2432             sub handle_user_macro { # [[[
2433 180     180 0 155 my $macro = $State{macro};
2434 180         206 my $perl = $Macro{$macro}{perl};
2435 180         118 my @processed_parse;
2436              
2437 180 100       246 unless ($perl) {
2438 94         92 my $parse = $Macro{$macro}{parse};
2439 94 100       127 unless (@$parse) {
2440 18         44 return;
2441             }
2442              
2443 76         78 foreach my $block (@$parse) {
2444 90         66 my $remaining = 0;
2445 90 100       152 if (@$block == 2) {
    50          
2446 38         37 my $t = $block->[0];
2447 38         103 $t =~ s{\\+\$(\d+)}{
2448 24 100 33     123 defined $Arg[$1-1] ? $Arg[$1-1] : (++$remaining and "\\\$$1");
2449             }xge;
2450 38         69 push @processed_parse, [ $t, $block->[1] ];
2451             }
2452             elsif (@$block == 3) {
2453 52         43 my $macro_name = $block->[0];
2454 52         32 my @macro_args = @{ $block->[1] };
  52         85  
2455             s{\\+\$(\d+)}{
2456 24 50 0     110 defined $Arg[$1-1] ? $Arg[$1-1] : (++$remaining and "\\\$$1");
2457 52         174 }xge for @macro_args;
2458 52         41 $macro_name =~ s{\\+\$(\d+)}{
2459 0 0 0     0 defined $Arg[$1-1] ? $Arg[$1-1] : (++$remaining and "\\\$$1");
2460             }xge;
2461 52         91 push @processed_parse,
2462             [ $macro_name, \@macro_args, $block->[2] ];
2463 52         29 my $remaining;
2464              
2465 52         48 foreach (@macro_args) {
2466 202 50 0     269 $remaining++ and last if /\\+\$\d/;
2467             }
2468             }
2469 90 100       146 diag_error("`$macro' invocation:not enough arguments provided")
2470             if $remaining;
2471             }
2472             }
2473              
2474             # Keep the line number of the call, the name of the macro, and the current
2475             # file name for better diags.
2476             # Don't permit recursive calls to erase this values as the first user macro
2477             # called is the one that is usefull in diagnostics.
2478 162 100       273 if ($UserMacroCall{depth} == 0) {
2479 154         143 $UserMacroCall{lnum} = $State{lnum};
2480 154         134 $UserMacroCall{name} = $macro;
2481 154         143 $UserMacroCall{file} = $File;
2482             }
2483 162         130 $UserMacroCall{depth}++;
2484 162 100       168 if ($perl) {
2485 86         110 $Flag{_perl} = 1;
2486 86         179 $Self->_call_perl_macro($macro);
2487 86         113 $Flag{_perl} = 0;
2488             }
2489             else {
2490 76 100       78 if ($Process) {
2491 38         49 process_source(\@processed_parse);
2492             }
2493             else {
2494 38         52 collect_source_infos(\@processed_parse);
2495             }
2496             }
2497 162         147 $UserMacroCall{depth}--;
2498 162 100       277 if ($UserMacroCall{depth} == 0) {
2499 154         116 $UserMacroCall{lnum} = undef;
2500 154         114 $UserMacroCall{name} = undef;
2501 154         269 $UserMacroCall{file} = undef;
2502             }
2503             } # ]]]
2504              
2505             ################################################################################
2506             # Utility functions, in alphabetic order.
2507              
2508             sub add_non_breaking_spaces { # [[[
2509 11     11 0 7 my $text = shift;
2510 11 100       19 if ($Flag{'fr-nbsp-auto'}) {
2511 8         90 $text =~ s/\h*(?:\\~)?(?
2512 8         27 $text =~ s/(\x{ab})(?!\\&)(?:\\~)?\h*/$1\\~/xg;
2513             }
2514 11         13 return $text;
2515             } # ]]]
2516              
2517             sub args_to_text { # [[[
2518 1018     1018 0 749 my $args = shift;
2519 1018 100       1342 my $sep = $Flag{ns} ? "" : " ";
2520 1018         1458 my $text = join($sep, @$args);
2521 1018         1558 return $text;
2522             } # ]]]
2523              
2524             sub call { # [[[
2525 12     12 0 24 my ($macro, @args) = @_;
2526 12         24 local $State{macro} = $macro;
2527 12         25 local @Arg = @args;
2528 12 100       23 if ($Process) {
2529 11         23 process_macro();
2530             }
2531             else {
2532 1         6 collect_macro_infos();
2533             }
2534             } # ]]]
2535              
2536             sub close_eventual_final_paragraph { # [[[
2537 434     434 0 311 my $last = shift;
2538 434 100       787 if ($Scope{paragraph}) {
    100          
2539 90         147 handle_paragraph_end($last);
2540             }
2541             elsif ($State{text}) {
2542 124         170 handle_paragraph($last);
2543             }
2544             } # ]]]
2545              
2546             sub close_spanning_blocks { # [[[
2547 275     275 0 213 my $stack = $Scope{Bm};
2548 275         354 foreach my $st (reverse @$stack) {
2549 10         13 my $begin_macro = $st->{macro};
2550              
2551 10         10 my $end;
2552 10 100       15 if (defined $st->{t}) {
2553 2         5 $end = enclose_end($Xmtag{ $st->{t} }{cmd});
2554             }
2555 10   66     24 $end //= enclose_end($Xmtag{_default}{cmd});
2556              
2557 10         12 print $end;
2558             }
2559             } # ]]]
2560              
2561             sub close_unclosed_blocks { # [[[
2562 1179     1179 0 953 my $type = shift;
2563 1179 100       1287 if (test_for_unclosed_block($type)) {
2564 6         8 local @Arg = ();
2565 6         13 local $State{macro} = $type;
2566 6         9 local $Flag{_no_warnings} = 1;
2567 6 100       15 if ($type eq "Bm") {
    50          
    50          
2568 4         5 handle_Em_macro while @{ $Scope{$type} };
  10         26  
2569             }
2570             elsif ($type eq "Bl") {
2571 0         0 handle_El_macro while @{ $Scope{$type} };
  0         0  
2572             }
2573             elsif ($type eq "Bd") {
2574 2         3 handle_Ed_macro while @{ $Scope{$type} };
  6         16  
2575             }
2576             }
2577             } # ]]]
2578              
2579             sub diag { # [[[
2580 149     149 0 111 my $message = shift;
2581 149 100       230 if (defined $UserMacroCall{lnum}) {
    50          
    0          
2582             return
2583 6         23 "frundis:$UserMacroCall{file}:$UserMacroCall{lnum}:in user macro `.$UserMacroCall{name}':$message\n";
2584             }
2585             elsif (defined $State{lnum}) {
2586 143         304 return "frundis:$File:$State{lnum}:$message\n";
2587             }
2588             elsif ($File) {
2589 0         0 return "frundis:$File:$message\n";
2590             }
2591             else {
2592 0         0 return "frundis:$message\n";
2593             }
2594             } # ]]]
2595              
2596             sub diag_error { # [[[
2597 120 50   120 0 168 return if $Flag{_no_warnings};
2598 120         106 my $message = shift;
2599 120         95 $Flag{_frundis_warning} = 1;
2600 120         183 $message = diag("error:$message");
2601 120 50       154 if ($Opts{use_carp}) {
2602 0         0 chomp $message;
2603 0         0 carp $message;
2604             }
2605             else {
2606 120         1366 warn $message;
2607             }
2608 120         166 $Flag{_frundis_warning} = 0;
2609             } # ]]]
2610              
2611             sub diag_fatal { # [[[
2612 0     0 0 0 my $message = shift;
2613 0         0 $message = diag("fatal:$message");
2614 0 0       0 if ($Opts{use_carp}) {
2615 0         0 chomp $message;
2616 0         0 croak $message;
2617             }
2618             else {
2619 0         0 die $message;
2620             }
2621             } # ]]]
2622              
2623             sub diag_warning { # [[[
2624 29 50   29 0 47 return if $Flag{_no_warnings};
2625 29         29 my $message = shift;
2626 29         19 $Flag{_frundis_warning} = 1;
2627 29         48 $message = diag("warning:$message");
2628 29 50       44 if ($Opts{use_carp}) {
2629 0         0 chomp $message;
2630 0         0 carp $message;
2631             }
2632             else {
2633 29         329 warn $message;
2634             }
2635 29         106 $Flag{_frundis_warning} = 0;
2636             } # ]]]
2637              
2638             sub enclose_begin { # [[[
2639 414     414 0 435 my ($elt, $opts) = @_;
2640 414 100       536 unless ($elt) {
2641 4         7 return "";
2642             }
2643 410 100       429 if (defined $opts) {
2644 346 50       596 diag_fatal(
2645             'internal error: enclose_begin: $opts is not a hash reference')
2646             unless ref $opts eq "HASH";
2647             }
2648             else {
2649 64         57 $opts = {};
2650             }
2651 410 100       1442 if ($Opts{target_format} eq "latex") {
    50          
2652 157 100       567 return $opts->{env} ? "\\begin{$elt}" : "\\$elt\{";
2653             }
2654             elsif ($Opts{target_format} =~ /$Rx{xhtml_or_epub}/) {
2655 253         216 my $attributes = "";
2656 253 100       399 if ($opts->{class}) {
2657 146         243 $attributes .= qq{ class="$opts->{class}"};
2658             }
2659 253 100       370 if ($opts->{id}) {
2660 137         173 $attributes .= qq{ id="$opts->{id}"};
2661             }
2662 253         742 return "<${elt}${attributes}>";
2663             }
2664             } # ]]]
2665              
2666             sub enclose_end { # [[[
2667 414     414 0 397 my ($elt, $opts) = @_;
2668 414 100       515 unless ($elt) {
2669 4         6 return "";
2670             }
2671 410 100       428 if (defined $opts) {
2672 84 50       154 diag_fatal('internal error: enclose_end: $opts is not a hash reference')
2673             unless ref $opts eq "HASH";
2674             }
2675             else {
2676 326         291 $opts = {};
2677             }
2678 410 100       1288 if ($Opts{target_format} eq "latex") {
    50          
2679 157 100       391 return $opts->{env} ? "\\end{$elt}" : '}';
2680             }
2681             elsif ($Opts{target_format} =~ /$Rx{xhtml_or_epub}/) {
2682 253         652 return "";
2683             }
2684             } # ]]]
2685              
2686             sub end_any_previous_item { # [[[
2687 51 100   51 0 84 if ($State{text}) {
2688 22         25 chomp $State{text};
2689 22         34 give_wanted_space();
2690 22         25 flush_normal_text();
2691             }
2692             } # ]]]
2693              
2694             sub escape { # [[[
2695 1010     1010 0 767 my $text = shift;
2696 1010         804 $text =~ s/(\\&|\\e|\\~)/$Frundis_escapes{$1}/gex;
  8         18  
2697 1010         1052 return $text;
2698             } # ]]]
2699              
2700             sub escape_latex_percent { # [[[
2701 21     21 0 17 my $text = shift;
2702              
2703             # for url and path arguments
2704 21         121 $text =~ s/%/\\%/g;
2705 21         75 return $text;
2706             } # ]]]
2707              
2708             sub escape_latex_text { # [[[
2709 692     692 0 549 my $text = shift;
2710              
2711 692         1043 $text =~ s/(\{|\}|\[|\]|%|&|\$|\#|_|\\|\^|~)/$Latex_escapes{$1}/gex;
  48         115  
2712 692         814 $text =~ tr/\x{a0}/~/;
2713              
2714 692         768 return $text;
2715             } # ]]]
2716              
2717             sub escape_text { # [[[
2718 1872     1872 0 1461 my $text = shift;
2719 1872 100 100     3186 if ($Param{lang} eq "fr" and not $Flag{_verbatim}) {
2720 11         15 $text = add_non_breaking_spaces($text);
2721             }
2722 1872         1917 $text =~ s/(\\&|\\e|\\~)/$Frundis_escapes{$1}/gex;
  130         285  
2723 1872 100       6062 if ($Opts{target_format} eq "latex") {
    50          
2724 692         739 $text = escape_latex_text($text);
2725             }
2726             elsif ($Opts{target_format} =~ /$Rx{xhtml_or_epub}/) {
2727 1180         1314 $text = escape_xhtml_text($text);
2728             }
2729 1872         2823 return $text;
2730             } # ]]]
2731              
2732             sub escape_verbatim { # [[[
2733 179     179 0 148 my $text = shift;
2734 179         385 $text =~ s/(\\&|\\e|\\~)/$Frundis_escapes{$1}/gex;
  135         299  
2735 179 100       455 $text =~ tr/\x{a0}/ / if $Opts{target_format} eq "latex";
2736 179         314 return $text;
2737             } # ]]]
2738              
2739             sub escape_xhtml_text { # [[[
2740 1225     1225 0 963 my $text = shift;
2741              
2742 1225         1885 $text =~ s/(&|<|>|"|')/$Xhtml_escapes{$1}/gex;
  54         158  
2743              
2744 1225         1442 return $text;
2745             } # ]]]
2746              
2747             sub flush_normal_text { # [[[
2748 773     773 0 882 $State{text} =~ s/\n\s*\n/\n/g;
2749 773         866 print $State{text};
2750 773         640 $State{wanted_space} = 0;
2751 773         661 $State{text} = "";
2752             } # ]]]
2753              
2754             sub get_close_delim { # [[[
2755 115     115 0 98 my $close_delim = "";
2756 115 100 100     783 if ( @Arg
      100        
2757 3     3   15 and $Arg[$#Arg] =~ /^(?:\\~)?\p{Punct}+$/
  3         16  
  3         33  
2758             and $Arg[$#Arg] !~ /^\\&/)
2759             {
2760 33         40 $close_delim = pop @Arg;
2761 33 50       65 if ($Param{lang} eq "fr") {
2762 0         0 $close_delim = add_non_breaking_spaces($close_delim);
2763             }
2764 33         45 $close_delim = escape_text($close_delim);
2765             }
2766 115         318 return $close_delim;
2767             } # ]]]
2768              
2769             sub give_wanted_space { # [[[
2770 767 100   767 0 1215 print "\n" if $State{wanted_space};
2771             } # ]]]
2772              
2773             sub handle_paragraph { # [[[
2774 161     161 0 135 my $last = shift;
2775 161         197 paragraph_begin();
2776 161         233 handle_paragraph_end($last);
2777             } # ]]]
2778              
2779             sub handle_paragraph_begin { # [[[
2780 87 50   87 0 152 unless ($Scope{paragraph}) {
2781 87         114 paragraph_begin();
2782             }
2783 87         135 give_wanted_space();
2784 87         104 flush_normal_text();
2785             } # ]]]
2786              
2787             sub handle_paragraph_end { # [[[
2788 275     275 0 226 my $last = shift;
2789 275         327 paragraph_end();
2790 275 100 100     729 if ($Opts{target_format} eq "latex" and not $last) {
2791 87         92 print "\n";
2792             }
2793 275         312 $Scope{paragraph} = 0;
2794             } # ]]]
2795              
2796             sub headers_count_update { # [[[
2797 366     366 0 432 my $nonum = shift;
2798 366         316 my $macro = $State{macro};
2799 366 100       780 if ($macro eq "Pt") {
    100          
    100          
    50          
2800 48         47 $Count{part}++;
2801 48 50       85 $Count{numbered_part}++ unless $nonum;
2802 48         44 $Count{section} = 0;
2803 48         51 $Count{subsection} = 0;
2804 48         43 $Count{numbered_section} = 0;
2805 48         63 $Count{numbered_subsection} = 0;
2806             }
2807             elsif ($macro eq "Ch") {
2808 150         158 $Count{chapter}++;
2809 150 100       247 $Count{numbered_chapter}++ unless $nonum;
2810 150         125 $Count{section} = 0;
2811 150         155 $Count{subsection} = 0;
2812 150         126 $Count{numbered_section} = 0;
2813 150         191 $Count{numbered_subsection} = 0;
2814             }
2815             elsif ($macro eq "Sh") {
2816 112         125 $Count{section}++;
2817 112 100       176 $Count{numbered_section}++ unless $nonum;
2818 112         96 $Count{numbered_subsection} = 0;
2819 112         96 $Count{subsection} = 0;
2820             }
2821             elsif ($macro eq "Ss") {
2822 56         47 $Count{subsection}++;
2823 56 50       100 $Count{numbered_subsection}++ unless $nonum;
2824             }
2825 366         410 $Count{header}++;
2826             } # ]]]
2827              
2828             sub header_level { # [[[
2829 435     435 0 348 my $header_macro = shift;
2830 435         300 my $level = -1;
2831 435 100       561 if ($InfosFlag{has_part}) {
    100          
2832 341         271 $level = 1;
2833             }
2834             elsif ($InfosFlag{has_chapter}) {
2835 86         68 $level = 0;
2836             }
2837             return
2838 435 100       1563 $header_macro eq "Pt" ? $level
    100          
    100          
2839             : $header_macro eq "Ch" ? $level + 1
2840             : $header_macro eq "Sh" ? $level + 2
2841             : $level + 3;
2842             } # ]]]
2843              
2844             sub header_number { # [[[
2845 183     183 0 180 my $nonum = shift;
2846 183 100       268 return "" if $nonum;
2847 174         148 my $macro = $State{macro};
2848 174         112 my $num;
2849 174 100       373 if ($macro eq "Pt") {
    100          
    100          
    50          
2850 24         37 $num = "$Count{numbered_part}";
2851             }
2852             elsif ($macro eq "Ch") {
2853 73         86 $num = "$Count{numbered_chapter}";
2854             }
2855             elsif ($macro eq "Sh") {
2856 49 100       65 if ($InfosFlag{has_chapter}) {
2857 43         84 $num = "$Count{numbered_chapter}.$Count{numbered_section}";
2858             }
2859             else {
2860 6         8 $num = "$Count{numbered_section}";
2861             }
2862             }
2863             elsif ($macro eq "Ss") {
2864 28 100       49 if ($InfosFlag{has_chapter}) {
    50          
2865 26         65 $num =
2866             "$Count{numbered_chapter}.$Count{numbered_section}.$Count{numbered_subsection}";
2867             }
2868             elsif ($Count{numbered_section}) {
2869 2         5 $num = "$Count{numbered_section}.$Count{numbered_subsection}";
2870             }
2871             else {
2872 0         0 $num = "0.$Count{numbered_subsection}";
2873             }
2874             }
2875 174         204 return $num;
2876             } # ]]]
2877              
2878             sub init_infos { # [[[
2879 60 100 66 60 0 230 if ($Opts{target_format} eq "latex") {
    50          
2880 27         385 %Param = (
2881             'dmark' => '---',
2882             'nbsp' => '~',
2883             _desc_name_begin => '\item[',
2884             _desc_name_end => "]\n",
2885             _desc_value_begin => '',
2886             _desc_value_end => "\n",
2887             _item_begin => '\item ',
2888             _item_end => "\n",
2889             _line_break => " \\\\\n",
2890             _list_desc => 'description',
2891             _list_enum => 'enumerate',
2892             _list_item => 'itemize',
2893             _list_table => 'tabular',
2894             _paragraph_begin => "",
2895             _paragraph_end => "\n",
2896             _poemtitle => 'poemtitle',
2897             _table_cell_begin => " & ",
2898             _table_cell_end => "",
2899             _table_row_begin => "",
2900             _table_row_end => " \\\\\n",
2901             _verse => 'verse',
2902             );
2903 27         81 %Xmtag = (_default => { cmd => 'emph' });
2904 27         68 %Xdtag = (_default => { cmd => '' });
2905             }
2906             elsif ($Opts{target_format} eq "xhtml" or $Opts{target_format} eq "epub") {
2907 33         568 %Param = (
2908             'dmark' => "\x{2014}",
2909             'nbsp' => "\x{a0}",
2910             'xhtml-index' => "full",
2911             'xhtml5' => "0",
2912             _desc_name_begin => '
',
2913             _desc_name_end => "\n",
2914             _desc_value_begin => '
',
2915             _desc_value_end => "\n",
2916             _item_begin => '
  • ',
  • 2917             _item_end => "\n",
    2918             _line_break => "
    \n",
    2919             _list_desc => 'dl',
    2920             _list_enum => 'ol',
    2921             _list_item => 'ul',
    2922             _list_table => 'table',
    2923             _paragraph_begin => "

    ",

    2924             _paragraph_end => "

    \n",
    2925             _poemtitle => "h4",
    2926             _table_cell_begin => "",
    2927             _table_cell_end => "
    2928             _table_row_begin => "
    2929             _table_row_end => "
    2930             _verse => '',
    2931             );
    2932 33         102 %Xmtag = (_default => { cmd => 'em' });
    2933 33         90 %Xdtag = (_default => { cmd => 'div' });
    2934             }
    2935              
    2936 60 100       127 if ($Opts{target_format} eq "epub") {
    2937 3         6 $Param{'epub-version'} = "2";
    2938             }
    2939             %loXstack = (
    2940 60         480 toc => [],
    2941             nav => [],
    2942             lot => [],
    2943             lof => [],
    2944             );
    2945 60         295 %InfosFlag = (
    2946             use_verse => 0,
    2947             use_minitoc => 0,
    2948             has_part => 0,
    2949             has_chapter => 0,
    2950             use_graphicx => 0,
    2951             dominilof => 0,
    2952             dominilot => 0,
    2953             dominitoc => 0,
    2954             );
    2955 60         87 $Param{lang} = "en";
    2956 60         75 $Param{_index} = "Index";
    2957 60 100       128 %Filters = defined $Opts{filters} ? %{ $Opts{filters} } : ();
      1         3  
    2958 60         65 %ID = ();
    2959 60         81 @Image = ();
    2960             } # ]]]
    2961              
    2962             sub init_state { # [[[
    2963 120     120 0 479 %State = (
    2964             lnum => undef, # current line number
    2965             macro => undef, # current macro name
    2966             text => "", # accumulated text
    2967             _table_title => undef,
    2968             _xhtml_navigation_text => "",
    2969             );
    2970 120         435 %Flag = (
    2971             'fr-nbsp-auto' => 1, # automatically add nbsps
    2972             _ignore_text => 0, # whether to ignore text lines
    2973             _frundis_warning => 0,
    2974             _no_warnings => 0,
    2975             ns => 0, # no-space mode
    2976             _perl => 0,
    2977             _verbatim => 0, # verbatim mode
    2978             );
    2979 120         503 %Scope = (
    2980             Bd => [], # list of nested .Bd macros
    2981             Bl => [], # list of nested .Bl macros
    2982             Bm => [], # list of nested .Bm macros
    2983             "#if" => [], # list of nested .#if macros
    2984             de => 0, # in macro definition
    2985             if_ignore => 0,
    2986             item => 0, # under a non closed
    2987             paragraph => 0, # under a non closed

    2988             );
    2989 120         198 reset_Bf_macro_state();
    2990 120         192 reset_de_macro_state();
    2991 120         253 %UserMacroCall = (
    2992             depth => 0,
    2993             file => undef,
    2994             lnum => undef,
    2995             name => undef,
    2996             );
    2997 120         525 %Count = (
    2998             chapter => 0,
    2999             fig => 0,
    3000             header => 0,
    3001             numbered_chapter => 0,
    3002             numbered_part => 0,
    3003             numbered_section => 0,
    3004             numbered_subsection => 0,
    3005             part => 0,
    3006             section => 0,
    3007             subsection => 0,
    3008             table => 0,
    3009             );
    3010 120 100       787 %Macro = defined $Opts{user_macros} ? %{ $Opts{user_macros} } : ();
      2         5  
    3011 120         242 $Self->{vars} = {};
    3012             } # ]]]
    3013              
    3014             sub interpolate_vars { # [[[
    3015 6023     6023 0 4436 my $text = shift;
    3016 6023         4402 my $vars = $Self->{vars};
    3017 6023         4769 $text =~ s|\\\*\[([^\]]*)\]|
    3018 22         33 my $name = $1;
    3019 22         22 my $repl = $vars->{$name};
    3020 22 100       31 if (defined $repl) { $repl }
      20         36  
    3021             else {
    3022 2         6 diag_warning("variable interpolation:undefined variable:$name");
    3023 2         6 "";
    3024             }
    3025             |gex;
    3026 6023         8548 return $text;
    3027             } # ]]]
    3028              
    3029             sub loX_entry_infos { # [[[
    3030 46     46 0 83 my $opts = shift;
    3031 46         59 my $title = $opts->{title};
    3032 46         50 my $count = $opts->{count};
    3033 46         47 my $class = $opts->{class};
    3034 46         40 my $prefix = $opts->{href_prefix};
    3035 46         75 my $href = xhtml_gen_href($prefix, $count);
    3036 46         155 $Self->{loX}{$class}{$title} = {
    3037             href => $href,
    3038             href_prefix => $prefix,
    3039             count => $count,
    3040             };
    3041 46 100       97 unless (defined $loXstack{$class}) {
    3042 6         12 $loXstack{$class} = [];
    3043             }
    3044              
    3045 46         43 push @{ $loXstack{$class} },
      46         296  
    3046             {
    3047             href_prefix => $prefix,
    3048             href => $href,
    3049             count => $count,
    3050             title => $title,
    3051             };
    3052             } # ]]]
    3053              
    3054             sub phrasing_macro_begin { # [[[
    3055 287     287 0 357 my $ns = shift;
    3056 287         288 chomp $State{text};
    3057 287 100 100     1355 if (!$Flag{ns} and !$ns and ($State{wants_space} or $State{text})) {
          66        
          66        
    3058 225 100       439 $State{text} .= $State{inline} ? " " : "\n";
    3059             }
    3060 287         346 phrasing_macro_handle_whitespace();
    3061             } # ]]]
    3062              
    3063             sub phrasing_macro_end { # [[[
    3064 44     44 0 54 chomp $State{text};
    3065 44         43 phrasing_macro_handle_whitespace();
    3066             } # ]]]
    3067              
    3068             sub phrasing_macro_handle_whitespace { # [[[
    3069 331 100 100 331 0 938 if (!$Scope{paragraph} and !$Scope{item} and !$State{inline}) {
    3070 87         131 handle_paragraph_begin();
    3071             }
    3072             else {
    3073 244         287 give_wanted_space();
    3074 244         273 flush_normal_text();
    3075             }
    3076 331         741 $State{wants_space} = !$Flag{ns};
    3077             } # ]]]
    3078              
    3079             sub paragraph_begin { # [[[
    3080 252     252 0 370 print $Param{_paragraph_begin};
    3081 252         310 reopen_spanning_blocks();
    3082 252         263 $Scope{paragraph} = 1;
    3083             } # ]]]
    3084              
    3085             sub paragraph_end { # [[[
    3086 275     275 0 311 chomp $State{text};
    3087 275         310 give_wanted_space();
    3088 275         288 flush_normal_text();
    3089 275         309 close_spanning_blocks();
    3090 275         330 print $Param{_paragraph_end};
    3091             } # ]]]
    3092              
    3093             sub parse_options { # [[[
    3094 1616     1616 0 1394 my ($spec, $cmd) = @_;
    3095 1616   66     3959 $cmd //= $State{macro};
    3096 1616         1127 my %opts;
    3097 1616         2273 while (@Arg) {
    3098 2131         1571 my $flag = $Arg[0];
    3099 2131 100       5047 last unless ($flag =~ s/^-//);
    3100 987         1305 $flag = escape($flag);
    3101 987         777 shift @Arg;
    3102 987 100       1514 unless ($spec->{$flag}) {
    3103              
    3104 4         21 diag_error("`$cmd' macro invocation: unrecognized option: -$flag");
    3105 4         8 next;
    3106             }
    3107 983 100       1576 if ($spec->{$flag} eq "s") {
        50          
    3108              
    3109             # string argument
    3110 724 100       859 unless (@Arg) {
    3111 4         12 diag_error(
    3112             "`$cmd' macro invocation: option -$flag requires an argument"
    3113             );
    3114 4         8 next;
    3115             }
    3116 720         542 my $arg = shift(@Arg);
    3117 720 50 33     2159 if (defined $arg and $arg !~ /^-/) {
    3118 720         1542 $opts{$flag} = $arg;
    3119             }
    3120             }
    3121             elsif ($spec->{$flag} eq "b") {
    3122              
    3123             # boolean flag
    3124 259         555 $opts{$flag} = 1;
    3125             }
    3126             }
    3127 1616         3562 return %opts;
    3128             } # ]]]
    3129              
    3130             sub parse_macro_line { # [[[
    3131 1621     1621 0 1267 my $text = shift;
    3132 1621         972 my $macro;
    3133 1621 50       4086 if ($text =~ s/^(\S+)//) {
    3134 1621         1652 $macro = $1;
    3135             }
    3136             else {
    3137 0         0 return ();
    3138             }
    3139 1621         1323 my @args;
    3140 1621         3092 while (
    3141             $text =~ /
    3142             \s*
    3143             (?|
    3144             "( (?| [^"] | "" )* ) "? # quoted string: "" is preserved inside
    3145             |
    3146             (\S+) # unquoted string
    3147             )
    3148             /xg
    3149             )
    3150             {
    3151 2709         2437 my $arg = $1;
    3152 2709         2084 $arg =~ s/""/"/g;
    3153 2709         5603 push @args, $arg;
    3154             }
    3155 1621         2754 return $macro, \@args;
    3156             } # ]]]
    3157              
    3158             sub print_file { # [[[
    3159 33     33 0 46 my ($file, $msg) = @_;
    3160 33 50       481 unless (-f $file) {
    3161 0         0 $file = search_inc_file($file);
    3162             }
    3163 33   100     68 $msg //= "";
    3164 33 50       838 open(my $fh, '<', $file)
    3165             or diag_fatal("$msg:$file:$!");
    3166 33         38 my $text;
    3167 33         34 { local $/; $text = <$fh>; }
      33         110  
      33         453  
    3168 33         174 close $fh;
    3169 33         128 print $text;
    3170             } # ]]]
    3171              
    3172             sub print_filter { # [[[
    3173 6     6 0 8 my ($cmd, $text) = @_;
    3174 6         747 require File::Temp;
    3175              
    3176 6         11638 my $tmp = File::Temp->new(EXLOCK => 0);
    3177 6     1   2177 binmode($tmp, ':encoding(utf-8)');
      1         6  
      1         2  
      1         6  
    3178              
    3179 6         1106 print $tmp $text;
    3180 6         17 local $?;
    3181 6         23 my $filtered_text = qx#<$tmp $cmd#;
    3182 6 50       20862 if ($?) {
    3183 0         0 diag_warning(
    3184             "`$State{macro}' macro:error in command '<$tmp $cmd':status $?:$filtered_text"
    3185             );
    3186             }
    3187             else {
    3188 6         32 print $filtered_text;
    3189             }
    3190 6         173 close $tmp;
    3191             } # ]]]
    3192              
    3193             sub process_inline_macros { # [[[
    3194 283     283 0 247 my $title_render = "";
    3195 283         594 local @Arg = @Arg;
    3196             {
    3197 283         198 local *STDOUT;
      283         440  
    3198 283 50       1918 open(STDOUT, '>', \$title_render) or die "redirecting stdout:$!";
    3199              
    3200             # parse arguments
    3201 283         447 my @arglist = ([]);
    3202 283         477 while (@Arg) {
    3203 467         399 my $word = shift @Arg;
    3204 467 100       851 if ($word =~ /^(?:Bm|Em|Sm)$/) {
    3205 28         52 push @arglist, [$word];
    3206             }
    3207             else {
    3208 439         280 push @{ $arglist[$#arglist] }, $word;
      439         1003  
    3209             }
    3210             }
    3211 283         424 local $State{wanted_space} = 0;
    3212 283         279 local $State{wants_space} = 0;
    3213 283         348 foreach my $args (@arglist) {
    3214 311 100       453 next unless @$args;
    3215 307 100       573 if ($args->[0] =~ /^(?:Bm|Em|Sm)$/) {
    3216 28         26 my $macro = shift @$args;
    3217 28         43 local $State{inline} = 1;
    3218 28         31 local $State{macro} = $macro;
    3219 28         45 local @Arg = @$args;
    3220 28         47 $BuiltinMacroHandler{$macro}->();
    3221             }
    3222             else {
    3223 279         381 print escape_text(args_to_text($args));
    3224 279         453 $State{wants_space} = 1;
    3225             }
    3226             }
    3227 283         871 close STDOUT;
    3228             }
    3229              
    3230 283         743 return Encode::decode_utf8($title_render);
    3231             } # ]]]
    3232              
    3233             sub reopen_spanning_blocks { # [[[
    3234 269     269 0 271 my $stack = $Scope{Bm};
    3235 269         447 foreach my $st (@$stack) {
    3236 10         13 my $begin_macro = $st->{macro};
    3237              
    3238 10         5 my $begin;
    3239 10 100       17 if (defined $st->{t}) {
    3240             $begin = enclose_begin(
    3241             $Xmtag{ $st->{t} }{cmd},
    3242             { class => $st->{t} }
    3243 2         10 );
    3244             }
    3245 10   66     33 $begin //= enclose_begin($Xmtag{_default}{cmd});
    3246              
    3247 10         12 print $begin;
    3248             }
    3249             } # ]]]
    3250              
    3251             sub reset_Bf_macro_state { # [[[
    3252 120     120 0 313 %BfMacro = (
    3253             begin_lnum => undef,
    3254             begin_file => undef,
    3255             in_macro => 0,
    3256             filter => undef,
    3257             );
    3258             } # ]]]
    3259              
    3260             sub reset_de_macro_state { # [[[
    3261 276     276 0 986 %DeMacro = (
    3262             text => "",
    3263             name => undef,
    3264             lnum => undef,
    3265             perl => 0,
    3266             ignore => 0,
    3267             file => undef,
    3268             );
    3269             } # ]]]
    3270              
    3271             sub scope_stack_push { # [[[
    3272 206     206 0 253 my ($type, $tag, $id) = @_;
    3273 206 50       339 $Scope{$type} = [] unless defined $Scope{$type};
    3274 206         1184 push @{ $Scope{$type} },
    3275             {
    3276             t => $tag,
    3277             id => $id,
    3278             macro => $State{macro},
    3279             lnum => $UserMacroCall{depth} > 0
    3280             ? $UserMacroCall{lnum}
    3281             : $State{lnum},
    3282             in_user_macro => $UserMacroCall{depth} > 0 ? 1 : 0,
    3283 206 100       141 file => $UserMacroCall{depth} > 0 ? $UserMacroCall{file} : $File,
        100          
        100          
    3284             };
    3285             } # ]]]
    3286              
    3287             sub search_inc_file { # [[[
    3288 0     0 0 0 my $file = shift;
    3289 0         0 foreach (@FrundisINC) {
    3290 0         0 my $fpath = catfile($_, $file);
    3291 0 0       0 if (-f $fpath) {
    3292 0         0 $file = $fpath;
    3293 0         0 last;
    3294             }
    3295             }
    3296 0         0 return $file;
    3297             } # ]]]
    3298              
    3299             sub slurp_file { # [[[
    3300 4     4 0 7 my ($file) = @_;
    3301 4 50       115 open(my $fh, '<', $file)
    3302             or diag_fatal("$file:$!");
    3303 4         5 my $text;
    3304 4         4 { local $/; $text = <$fh>; }
      4         11  
      4         50  
    3305 4         20 close $fh;
    3306 4         14 return $text;
    3307             } # ]]]
    3308              
    3309             sub test_for_unclosed_block { # [[[
    3310 1239     1239 0 959 my ($type) = @_;
    3311 1239         1041 my $stack = $Scope{$type};
    3312 1239 100       1459 if (@$stack) {
    3313 8         9 my $st = $stack->[ $#{$stack} ];
      8         11  
    3314 8         9 my $begin_macro = $st->{macro};
    3315 8         12 my $end_macro = $BlockEnd{$begin_macro};
    3316 8 50       16 my $Bfile = $File eq $st->{file} ? "" : " of file $st->{file}";
    3317             my $in_user_macro =
    3318 8 50       13 $st->{in_user_macro} ? " opened inside user macro" : "";
    3319 8 50       10 my $type = $st->{t} ? " of type $st->{t} " : "";
    3320              
    3321 8         8 my $macro = $State{macro};
    3322 8 100       16 $macro = "`.$macro' macro" if $macro ne "End Of File";
    3323             my $msg =
    3324             !$State{inline}
    3325 8 50       39 ? "found $macro while `.$begin_macro' macro${type}${in_user_macro} at line"
    3326             . " $st->{lnum}$Bfile isn't closed yet by a `.$end_macro'"
    3327             : "unclosed inline markup block${type}${in_user_macro}";
    3328 8         13 diag_error($msg);
    3329 8         18 return 1;
    3330             }
    3331 1231         1720 return 0;
    3332             } # ]]]
    3333              
    3334             sub test_for_unclosed_de { # [[[
    3335 60 100   60 0 111 if ($Scope{de}) {
    3336 2         9 diag_error("found End Of File while `.#de' macro at line"
    3337             . " $DeMacro{lnum} of file $DeMacro{file} isn't closed by a `.#.'"
    3338             );
    3339             }
    3340             } # ]]]
    3341              
    3342             sub test_for_unclosed_format_block { # [[[
    3343 1587 100   1587 0 1954 if ($Scope{format}) {
    3344             my $Bf_file =
    3345             $File eq $BfMacro{begin_file}
    3346 2 50       10 ? ""
    3347             : " of file $BfMacro{begin_file}";
    3348             my $in_user_macro =
    3349 2 50       4 $BfMacro{in_macro} ? " opened inside user macro" : "";
    3350 2         14 diag_error("`.$State{macro}' not allowed inside scope of "
    3351             . "`.Bf' macro$in_user_macro at line $BfMacro{begin_lnum}$Bf_file"
    3352             );
    3353 2         7 return 1;
    3354             }
    3355 1585         4609 return 0;
    3356             } # ]]]
    3357              
    3358             sub test_if_not_allowed_macro { # [[[
    3359 1556     1556 0 1121 my $macro = shift;
    3360 1556 100 100     3062 if ($macro !~ /^Ef$/ and test_for_unclosed_format_block()) {
        100 100        
        100 100        
          100        
    3361 2         5 return 1;
    3362             }
    3363             elsif ($Flag{_verbatim} and $macro !~ /^Ef|Ed$/) {
    3364 1         4 diag_error(
    3365             "`$macro' macro is not allowed inside `.Bf' or `.Bd -t literal' macro scope"
    3366             );
    3367 1         3 return 1;
    3368             }
    3369 1553         4178 elsif ( @{ $Scope{Bl} }
    3370             and $Scope{Bl}->[0]->{t} ne "verse"
    3371             and not $AllowedInBl{$macro})
    3372             {
    3373 2         9 diag_error(
    3374             "`.$macro' macro not allowed inside list of type ``$Scope{Bl}->[0]->{t}''"
    3375             );
    3376 2         7 return 1;
    3377             }
    3378 1551         3096 return 0;
    3379             } # ]]]
    3380              
    3381             ################################################################################
    3382             # Format specific functions, in alphabetic order.
    3383              
    3384             sub epub_copy_images { # [[[
    3385 3     3 0 17 my $images_dir = catdir($Opts{output_file}, "EPUB", "images");
    3386 3 50       39 unless (-d $images_dir) {
    3387             mkdir $images_dir
    3388             or diag_fatal("$images_dir:$!")
    3389 3 100 33     74 unless not @Image and not defined $Param{'epub-cover'};
          66        
    3390             }
    3391              
    3392 3         10 foreach my $image (@Image, $Param{'epub-cover'}) {
    3393 5 100       441 next unless $image;
    3394 4         157 my $image_name = basename($image);
    3395 4 50       52 unless (-f $image) {
    3396 0         0 $image = search_inc_file($image);
    3397             }
    3398 4 50       25 unless (-f $image) {
    3399 0         0 diag_fatal("image copy:$image:no such file");
    3400             }
    3401 4         25 my $new_image = catfile($images_dir, $image_name);
    3402 4 100       41 next if -f $new_image;
    3403 2 50       11 copy($image, $new_image)
    3404             or diag_fatal("image copy:$image to $new_image:$!");
    3405             }
    3406             } # ]]]
    3407              
    3408             sub epub_gen { # [[[
    3409 3 50   3 0 7 unless ($Param{'document-title'}) {
    3410 0         0 diag_error("EPUB requires document-title parameter to be set");
    3411             }
    3412 3   50     10 my $title = $Param{'document-title'} // "";
    3413 3         6 my $lang = $Param{lang};
    3414              
    3415 3         7 epub_gen_mimetype();
    3416              
    3417 3         10 epub_copy_images();
    3418              
    3419             # now 'epub-cover' is copied: preserve only the name
    3420 3         7 my $cover = $Param{'epub-cover'};
    3421 3 100       42 $cover = basename($cover) if $cover;
    3422              
    3423 3         10 epub_gen_container();
    3424              
    3425 3         18 epub_gen_content_opf($title, $lang, $cover);
    3426              
    3427 3 100       13 if ($Param{'epub-version'} =~ /^3/) {
    3428 1         4 epub_gen_nav($title);
    3429             }
    3430              
    3431 3         10 epub_gen_css();
    3432              
    3433 3         10 epub_gen_ncx($title);
    3434              
    3435 3 100       9 if ($cover) {
    3436 2         6 epub_gen_cover($title, $cover);
    3437             }
    3438              
    3439             } # ]]]
    3440              
    3441             sub epub_gen_container { # [[[
    3442             my $container_xml =
    3443 3     3 0 14 catfile($Opts{output_file}, "META-INF", "container.xml");
    3444 3 50       146 open(my $fh, '>', $container_xml)
    3445             or diag_fatal("$container_xml:$!");
    3446              
    3447 3         18 print $fh <
    3448            
    3449             EOS
    3450              
    3451 3         8 print $fh <
    3452            
    3453            
    3454            
    3455            
    3456            
    3457             EOS
    3458 3         62 close $fh;
    3459             } # ]]]
    3460              
    3461             sub epub_gen_content_opf { # [[[
    3462 3     3 0 6 my ($title, $lang, $cover) = @_;
    3463 3         17 my $content_opf = catfile($Opts{output_file}, 'EPUB', 'content.opf');
    3464 3         7 local *STDOUT;
    3465 3 50       131 open(STDOUT, '>', $content_opf) or diag_fatal($!);
    3466              
    3467             # EPUB/content.opf
    3468 3         13 print <
    3469            
    3470             EOS
    3471 3         4 my $deterministic;
    3472 3 50       18 if (defined $Param{'epub-uuid'}) {
    3473 3         5 $deterministic = 1;
    3474             }
    3475              
    3476 3 50       9 unless (defined $Param{'epub-uuid'}) {
    3477              
    3478 0         0 local $@;
    3479 0         0 eval 'require Data::UUID;';
    3480 0 0       0 if ($@) {
    3481 0         0 diag_warning(
    3482             "Data::UUID module not found, falling back to use system time as unique id for epub"
    3483             );
    3484 0         0 $Param{'epub-uuid'} = "epoch:" . time;
    3485             }
    3486             else {
    3487 0         0 my $ug = Data::UUID->new;
    3488 0         0 my $uuid = $ug->create();
    3489 0         0 $Param{'epub-uuid'} = "urn:uuid:" . $ug->to_string($uuid);
    3490             }
    3491             }
    3492 3         10 chomp $Param{'epub-uuid'};
    3493 3 100       15 print <
    3494            
    3495             EOS
    3496 3 100       18 print <
    3497            
    3498             EOS
    3499 3         19 print <
    3500            
    3501             xmlns:dcterms="http://purl.org/dc/terms/"
    3502             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    3503             xmlns:opf="http://www.idpf.org/2007/opf">
    3504             $Param{'epub-uuid'}
    3505             EOS
    3506 3         24 print <
    3507             $lang
    3508             $title
    3509             EOS
    3510 3 100       11 if ($Param{'epub-version'} =~ /^3/) {
    3511 1         465 require POSIX;
    3512 1         4717 my $time;
    3513 1 50       5 if ($deterministic) {
    3514 1         5 $time = "0001-01-01T01:01:01Z";
    3515             }
    3516             else {
    3517 0         0 $time = POSIX::strftime("%Y-%m-%dT%H:%M:%SZ", gmtime);
    3518             }
    3519 1 50       11 print <
    3520             $time
    3521             EOS
    3522             }
    3523 3 100       10 if ($Param{'epub-subject'}) {
    3524 2         8 print <
    3525             $Param{'epub-subject'}
    3526             EOS
    3527             }
    3528 3 100       10 if ($Param{'document-author'}) {
    3529 2         7 print <
    3530             $Param{'document-author'}
    3531             EOS
    3532             }
    3533 3 100 100     55 print <
    3534            
    3535             EOS
    3536 3 100       8 if ($Param{'epub-metadata'}) {
    3537 2         5 print_file($Param{'epub-metadata'}, "epub-metadata");
    3538             }
    3539 3         6 print <
    3540            
    3541            
    3542             EOS
    3543 3 100       14 print <
    3544            
    3545             href="nav.xhtml"
    3546             properties="nav"
    3547             media-type="application/xhtml+xml" />
    3548             EOS
    3549 3         6 print <
    3550            
    3551             href="toc.ncx"
    3552             media-type="application/x-dtbncx+xml" />
    3553             EOS
    3554              
    3555 3 100       7 if ($cover) {
    3556 2         9 my $cover_path = catfile('images', $cover);
    3557 2         8 print <
    3558            
    3559             href="$cover_path"
    3560             EOS
    3561             }
    3562 3 100 100     17 print <
    3563             properties="cover-image"
    3564             EOS
    3565 3 100       8 print <
    3566             media-type="image/jpeg" />
    3567             EOS
    3568 3 100       7 print <
    3569            
    3570             href="cover.xhtml"
    3571             media-type="application/xhtml+xml" />
    3572             EOS
    3573              
    3574 3         6 print <
    3575            
    3576             EOS
    3577 3         3 foreach (@{ $loXstack{toc} }) {
      3         10  
    3578 30 100       71 next unless $_->{macro} =~ /^(?:Pt|Ch)$/;
    3579 15         11 my $href = $_->{href};
    3580 15         12 my $id = $_->{id};
    3581 15         30 print <
    3582            
    3583             EOS
    3584             }
    3585 3         7 print <
    3586            
    3587             href="stylesheet.css"
    3588             media-type="text/css" />
    3589             EOS
    3590 3         4 foreach my $image_name (@Image) {
    3591 2         3 my $media_type;
    3592 2 50       12 if ($image_name =~ /\.png$/) {
        0          
        0          
        0          
    3593 2         3 $media_type = "image/png";
    3594             }
    3595             elsif ($image_name =~ /\.jpe?g$/) {
    3596 0         0 $media_type = "image/jpeg";
    3597             }
    3598             elsif ($image_name =~ /\.gif$/) {
    3599 0         0 $media_type = "image/gif";
    3600             }
    3601             elsif ($image_name =~ /\.svg$/) {
    3602 0         0 $media_type = "image/svg";
    3603             }
    3604 2         57 my $image_bname = basename($image_name);
    3605 2         9 my $image_path = catfile('images', $image_bname);
    3606 2         6 $image_bname = escape_xhtml_text($image_bname);
    3607 2         4 $image_path = escape_xhtml_text($image_path);
    3608 2         12 print <
    3609            
    3610             href="$image_path"
    3611             media-type="$media_type" />
    3612             EOS
    3613             }
    3614              
    3615 3         6 print <
    3616            
    3617            
    3618             EOS
    3619 3 100       8 print <
    3620            
    3621             EOS
    3622 3         4 print <
    3623            
    3624             EOS
    3625 3         3 foreach (@{ $loXstack{toc} }) {
      3         7  
    3626 30 100       53 next unless $_->{macro} =~ /^(?:Pt|Ch)$/;
    3627 15         15 my $name = $_->{id};
    3628 15         17 print <
    3629            
    3630             EOS
    3631             }
    3632 3 100       11 print <
    3633            
    3634             EOS
    3635 3         5 print <
    3636            
    3637            
    3638             EOS
    3639 3 100       9 print <
    3640            
    3641             EOS
    3642 3         4 print <
    3643            
    3644             EOS
    3645 3         94 print <
    3646            
    3647             EOS
    3648             } # ]]]
    3649              
    3650             sub epub_gen_cover { # [[[
    3651 2     2 0 4 my ($title, $cover) = @_;
    3652 2         20 my $cover_xhtml = catfile($Opts{output_file}, 'EPUB', 'cover.xhtml');
    3653 2         4 local *STDOUT;
    3654 2 50       91 open(STDOUT, '>', $cover_xhtml) or diag_fatal("$cover_xhtml:$!");
    3655 2         11 print <
    3656            
    3657             EOS
    3658 2         7 xhtml_and_epub_common_header();
    3659 2         56 print <
    3660             $title
    3661            
    3662            
    3663            
    3664            
    3665             cover image
    3666            
    3667            
    3668            
    3669             EOS
    3670              
    3671             } # ]]]
    3672              
    3673             sub epub_gen_css { # [[[
    3674 3     3 0 5 my $css_text = "";
    3675 3 100       9 if ($Param{'epub-css'}) {
    3676 2 50       23 unless (-f $Param{'epub-css'}) {
    3677 0         0 $Param{'epub-css'} = search_inc_file($Param{'epub-css'});
    3678             }
    3679 2 50       43 open(my $fh, '<', "$Param{'epub-css'}")
    3680             or diag_fatal("parameter epub-css:$Param{'epub-css'}:$!");
    3681 2         6 local $/;
    3682 2         24 $css_text = <$fh>;
    3683 2         12 close $fh;
    3684             }
    3685 3         17 my $stylesheet_css = catfile($Opts{output_file}, 'EPUB', 'stylesheet.css');
    3686 3 50       139 open(my $fh, '>', $stylesheet_css)
    3687             or diag_fatal("$stylesheet_css:$!");
    3688              
    3689             # EPUB/stylesheet.css
    3690 3         10 print $fh $css_text;
    3691 3         18 close $fh;
    3692             } # ]]]
    3693              
    3694             sub epub_gen_mimetype { # [[[
    3695 3     3 0 5 my $mimetype = "application/epub+zip";
    3696 3         14 my $mimetype_path = catfile($Opts{output_file}, 'mimetype');
    3697 3 50       163 open(my $fh, '>', $mimetype_path)
    3698             or diag_fatal("$mimetype_path:$!");
    3699 3         42 print $fh $mimetype;
    3700 3         93 close $fh;
    3701             } # ]]]
    3702              
    3703             sub epub_gen_nav { # [[[
    3704 1     1 0 2 my $title = shift;
    3705 1         6 my $nav_xhtml = catfile($Opts{output_file}, 'EPUB', 'nav.xhtml');
    3706 1         2 local *STDOUT;
    3707 1 50       60 open(STDOUT, '>', $nav_xhtml)
    3708             or diag_fatal("$nav_xhtml:$!");
    3709 1         8 print <
    3710            
    3711            
    3712            
    3713             xmlns:epub="http://www.idpf.org/2007/ops">
    3714            
    3715            
    3716             EOS
    3717 1 50       6 print <
    3718             $title
    3719            
    3720            
    3721            
    3722             EOS
    3723 1         2 print <
    3724              
    3725             EOS
    3726              
    3727 1         5 xhtml_toc("nav");
    3728             print_file($Param{'epub-nav-landmarks'})
    3729 1 50       4 if $Param{'epub-nav-landmarks'};
    3730              
    3731 1         24 print <
    3732            
    3733            
    3734             EOS
    3735             } # ]]]
    3736              
    3737             sub epub_gen_ncx { # [[[
    3738 3     3 0 7 my ($title) = @_;
    3739 3         16 my $toc_ncx = catfile($Opts{output_file}, 'EPUB', 'toc.ncx');
    3740 3         5 local *STDOUT;
    3741 3 50       177 open(STDOUT, '>', $toc_ncx)
    3742             or diag_fatal("$toc_ncx:$!");
    3743              
    3744 3         31 print <
    3745            
    3746            
    3747            
    3748            
    3749            
    3750            
    3751            
    3752            
    3753            
    3754             EOS
    3755 3 50       14 print <
    3756            
    3757             $title
    3758            
    3759             EOS
    3760 3         9 xhtml_toc("ncx");
    3761 3         74 print <
    3762            
    3763             EOS
    3764             } # ]]]
    3765              
    3766             sub latex_document_begin { # [[[
    3767 2     2 0 6 my $lang = $Param{lang};
    3768 2   50     8 my $lang_babel = $Lang_babel{$lang} // "english";
    3769 2   50     8 my $lang_mini = $Lang_mini{$lang} // "english";
    3770              
    3771 2   50     8 my $title = $Param{'document-title'} // "";
    3772 2   100     10 my $author = $Param{'document-author'} // "";
    3773 2   100     9 my $date = $Param{'document-date'} // "";
    3774 2 100       6 if ($Param{'latex-preamble'}) {
    3775 1         4 print_file($Param{'latex-preamble'}, "latex-preamble");
    3776             }
    3777             else {
    3778 1 50 33     5 if ($InfosFlag{has_chapter} or $InfosFlag{has_part}) {
    3779 1         5 print <
    3780             \\documentclass[a4paper,11pt]{book}
    3781             EOS
    3782             }
    3783             else {
    3784 0         0 print <
    3785             \\documentclass[a4paper,11pt]{article}
    3786             EOS
    3787             }
    3788 1         3 print <
    3789             \\usepackage[T1]{fontenc}
    3790             \\usepackage[utf8]{inputenc}
    3791             \\usepackage[$lang_babel]{babel}
    3792             EOS
    3793 1 50       5 print <
    3794             \\usepackage[$lang_mini]{minitoc}
    3795             EOS
    3796 1 50       3 print <
    3797             \\usepackage{verse}
    3798             EOS
    3799 1 50       3 print <
    3800             \\usepackage{graphicx}
    3801             EOS
    3802 1         6 print <
    3803             \\usepackage{verbatim}
    3804             \\usepackage[linkcolor=blue,colorlinks=true]{hyperref}
    3805              
    3806             \\title{$title}
    3807             \\author{$author}
    3808             \\date{$date}
    3809             EOS
    3810             }
    3811              
    3812 2         4 print "\\begin{document}\n";
    3813              
    3814 2 50       7 print "\\dominilof\n" if $InfosFlag{dominilof};
    3815 2 50       5 print "\\dominilot\n" if $InfosFlag{dominilot};
    3816 2 50       17 print "\\dominitoc\n" if $InfosFlag{dominitoc};
    3817              
    3818 2 100       8 print <
    3819             \\maketitle
    3820             EOS
    3821             }
    3822              
    3823             sub latex_document_end {
    3824 2     2 0 152 print <
    3825              
    3826             \\end{document}
    3827             EOS
    3828             } # ]]]
    3829              
    3830             sub latex_header_name { # [[[
    3831 114     114 0 89 my $macro = shift;
    3832             return
    3833             $macro eq "Ch" ? "chapter"
    3834             : $macro eq "Sh" ? "section"
    3835             : $macro eq "Ss" ? "subsection"
    3836             : $macro eq "Pt" ? "part"
    3837 114 50       243 : do { diag_error("internal_error:latex_header_name"); "section" };
      0 100       0  
      0 100       0  
        100          
    3838             } # ]]]
    3839              
    3840             sub xhtml_and_epub_common_header { # [[[
    3841 41 100 100 41 0 384 if ( $Opts{target_format} eq "epub" and $Param{'epub-version'} =~ /^3/
          66        
          66        
    3842             or $Opts{target_format} eq "xhtml" and $Param{'xhtml5'})
    3843             {
    3844 14         70 print <
    3845            
    3846             EOS
    3847             }
    3848             else {
    3849 27         97 print <
    3850            
    3851             EOS
    3852             }
    3853 41         112 print <
    3854            
    3855            
    3856             EOS
    3857 41 100 100     121 if ($Opts{target_format} eq "epub" and $Param{'epub-version'} =~ /^3/) {
    3858 7         11 print <
    3859            
    3860             EOS
    3861             }
    3862             else {
    3863 34         48 print <
    3864            
    3865             EOS
    3866             }
    3867              
    3868             } # ]]]
    3869              
    3870             sub xhtml_document_header { # [[[
    3871 39     39 0 52 my $title = shift;
    3872              
    3873 39         64 xhtml_and_epub_common_header();
    3874              
    3875 39 50       110 print <
    3876             $title
    3877             EOS
    3878 39 50       76 print <
    3879            
    3880             EOS
    3881 39 100 100     189 if ($Param{'epub-css'} and $Opts{target_format} eq "epub") {
        100 66        
    3882 12         17 print <
    3883            
    3884             EOS
    3885             }
    3886             elsif ($Param{'xhtml-css'} and $Opts{target_format} eq "xhtml") {
    3887 14         38 print <
    3888            
    3889             EOS
    3890             }
    3891 39         44 print <
    3892            
    3893            
    3894             EOS
    3895 39 100 66     104 if ($Opts{target_format} ne "epub" and $Param{'xhtml-top'}) {
    3896 14         29 print_file($Param{'xhtml-top'}, "xhtml-top");
    3897             }
    3898             } # ]]]
    3899              
    3900             sub xhtml_document_footer { # [[[
    3901 39 100 66 39 0 108 if ($Opts{target_format} ne "epub" and $Param{'xhtml-bottom'}) {
    3902 14         26 print_file($Param{'xhtml-bottom'}, "xhtml-bottom");
    3903             }
    3904 39         472 print <
    3905            
    3906            
    3907             EOS
    3908             } # ]]]
    3909              
    3910             sub xhtml_file_output_change { # [[[
    3911 30     30 0 22 my $title = shift;
    3912              
    3913 30 100 66     83 if ($Opts{target_format} ne "epub" and $State{_xhtml_navigation_text}) {
    3914 12         21 print $State{_xhtml_navigation_text};
    3915             }
    3916 30         43 xhtml_document_footer();
    3917              
    3918 30         23 my $out_file;
    3919 30 100       59 if ($Opts{target_format} eq "epub") {
    3920             $out_file = catfile(
    3921 15         88 $Opts{output_file}, 'EPUB',
    3922             "body-$Count{part}-$Count{chapter}.xhtml"
    3923             );
    3924             }
    3925             else {
    3926             $out_file =
    3927 15         88 catfile($Opts{output_file}, "body-$Count{part}-$Count{chapter}.html");
    3928             }
    3929 30 50       2306 open(STDOUT, '>', $out_file) or diag_fatal("$out_file:$!");
    3930 30         68 xhtml_document_header($title);
    3931              
    3932 30 100       67 return if $Opts{target_format} eq "epub";
    3933              
    3934             # IF NOT EPUB
    3935              
    3936 15         13 my ($previous, $next);
    3937             $previous = $loXstack{nav}->[ $State{nav_count} - 2 ]
    3938 15 100       41 unless $State{nav_count} <= 1;
    3939             $next = $loXstack{nav}->[ $State{nav_count} ]
    3940 15 100       18 unless $State{nav_count} >= @{ $loXstack{nav} };
      15         36  
    3941              
    3942 15         24 $State{_xhtml_navigation_text} = <
    3943            
    3944            
    3945             EOS
    3946 15 100       23 if (defined $previous) {
    3947 12         14 my $href = $previous->{href};
    3948 12         27 $State{_xhtml_navigation_text} .= <
    3949            
  • <
  • 3950             EOS
    3951             }
    3952             else {
    3953 3         10 $State{_xhtml_navigation_text} .= <
    3954            
  • <
  • 3955             EOS
    3956             }
    3957 15         24 $State{_xhtml_navigation_text} .= <
    3958            
  • $Param{_index}
  • 3959             EOS
    3960 15 100       23 if (defined $next) {
    3961 12         15 my $href = $next->{href};
    3962 12         23 $State{_xhtml_navigation_text} .= <
    3963            
  • >
  • 3964             EOS
    3965             }
    3966             else {
    3967 3         4 $State{_xhtml_navigation_text} .= <
    3968            
  • >
  • 3969             EOS
    3970             }
    3971 15         14 $State{_xhtml_navigation_text} .= <
    3972            
    3973            
    3974             EOS
    3975 15         37 print $State{_xhtml_navigation_text};
    3976              
    3977             } # ]]]
    3978              
    3979             sub xhtml_loX { # [[[
    3980 11     11 0 21 my ($class) = @_;
    3981             diag_warning("frundis:warning:no '$class' information found, skipping\n")
    3982             and return
    3983             unless defined $loXstack{$class}
    3984 11 50 0     35 and @{ $loXstack{$class} };
      11   33     38  
    3985 11         31 print qq{
    \n};
    3986 11         14 print qq{
      \n};
    3987              
    3988 11         10 foreach my $entry (@{ $loXstack{$class} }) {
      11         27  
    3989 26         43 xhtml_toc_like_entry($entry, {}, 1);
    3990             }
    3991 11         15 print qq{ \n};
    3992 11         82 print qq{\n};
    3993             } # ]]]
    3994              
    3995             sub xhtml_gen_href { # [[[
    3996 248     248 0 267 my ($prefix, $count, $hasfile) = @_;
    3997 248         162 my $href;
    3998 248 100       328 if ($Opts{all_in_one_file}) {
        100          
    3999 168         219 $href = "#$prefix$count";
    4000             }
    4001             elsif ($hasfile) {
    4002 30 100       51 my $suffix = $Opts{target_format} eq "epub" ? ".xhtml" : ".html";
    4003 30         59 $href = "body-$Count{part}-$Count{chapter}" . $suffix;
    4004             }
    4005             else {
    4006 50 100       75 my $suffix = $Opts{target_format} eq "epub" ? ".xhtml" : ".html";
    4007             $href =
    4008             ($Count{part} || $Count{chapter})
    4009 50 100 66     178 ? "body-$Count{part}-$Count{chapter}$suffix#$prefix$count"
    4010             : "index$suffix#$prefix$count";
    4011             }
    4012 248         388 return $href;
    4013             } # ]]]
    4014              
    4015             sub xhtml_lof { # [[[
    4016 0     0 0 0 xhtml_loX("lof");
    4017             } # ]]]
    4018              
    4019             sub xhtml_lot { # [[[
    4020 7     7 0 22 xhtml_loX("lot");
    4021             } # ]]]
    4022              
    4023             sub xhtml_section_header { # [[[
    4024 254     254 0 236 my $macro = shift;
    4025 254         329 return "h" . header_level($macro);
    4026             } # ]]]
    4027              
    4028             sub xhtml_titlepage { # [[[
    4029 9 100   9 0 26 if ($Param{'title-page'}) {
    4030             warn
    4031             "frundis:warning:parameter ``title-page'' set to 1 but no document title specified\n"
    4032 6 50       16 unless $Param{'document-title'};
    4033             warn
    4034             "frundis:warning:parameter ``title-page'' set to 1 but no document date specified\n"
    4035 6 50       15 unless $Param{'document-date'};
    4036             warn
    4037             "frundis:warning:parameter ``title-page'' set to true value but no document "
    4038             . "author specified with ``document-author'' parameter\n"
    4039 6 50       14 unless $Param{'document-author'};
    4040 6 50       29 print <
    4041            

    $Param{'document-title'}

    4042             EOS
    4043 6 50       37 print <
    4044            

    $Param{'document-author'}

    4045             EOS
    4046 6 50       23 print <
    4047            

    $Param{'document-date'}

    4048             EOS
    4049             }
    4050             } # ]]]
    4051              
    4052             sub xhtml_toc { # [[[
    4053 40     40 0 53 my ($type, $opts) = @_;
    4054             diag_warning(
    4055             "frundis:warning:no TOC information found, skipping TOC generation\n")
    4056             and return
    4057 40 50 0     31 unless @{ $loXstack{toc} };
      40         90  
    4058 40   100     84 $opts //= {};
    4059 40         60 $opts->{prefix} = "s";
    4060 40         38 $opts->{toc} = 1;
    4061 40         36 my $start = 0;
    4062 40         33 my $mini_macro = "Ch";
    4063 40 100 66     98 if ($opts->{mini} and $State{nav_count}) {
    4064 20         38 my $nav_entry = $loXstack{nav}->[ $State{nav_count} - 1 ];
    4065 20         24 $start = $nav_entry->{count};
    4066 20         25 $mini_macro = $nav_entry->{macro};
    4067             }
    4068              
    4069 40 100       82 my $close_list =
        100          
    4070             $type eq "ncx" ? ""
    4071             : $type eq "nav" ? ""
    4072             : "";
    4073 40 50       75 my $close_item =
        100          
        100          
    4074             $type eq "ncx" ? ""
    4075             : $type eq "xhtml" ? ""
    4076             : $type eq "nav" ? ""
    4077             : diag_error("internal_error:xhtml_toc");
    4078              
    4079             # TOC top
    4080 40 100       76 if ($type eq "ncx") {
        100          
        50          
    4081 3         6 print "\n";
    4082 3   50     9 my $title = $Param{'document-title'} // "";
    4083 3         12 print <
    4084            
    4085             $title
    4086            
    4087            
    4088             EOS
    4089             }
    4090             elsif ($type eq "xhtml") {
    4091 36         82 print q{
    }, "\n";
    4092 36         28 my $title;
    4093 36 100 100     102 if ($opts->{mini} or defined $opts->{title}) {
    4094 28         33 $title = $opts->{title};
    4095             }
    4096             else {
    4097 8         13 $title = $Param{'document-title'};
    4098             }
    4099 36 100       78 print <
    4100            

    $title

    4101             EOS
    4102 36         46 print "
      \n";
    4103             }
    4104             elsif ($type eq "nav") {
    4105 1         2 print qq{
    4106 1 50       6 print <
    4107            

    $Param{'document-title'}

    4108             EOS
    4109 1         2 print "
      \n";
    4110             }
    4111              
    4112             # TOC entries
    4113             # $level: the actual depth level of the entry in TOC.
    4114             # $title_level: the level of the title (1 for Pt, 2 for Ch, etc.)
    4115             # $previous_title_level: the level of the previous title
    4116 40         36 my $level = 0; # 0 for first iteration
    4117 40         31 my $previous_title_level = 1;
    4118 40         41 for (my $i = $start ; $i <= $#{ $loXstack{toc} } ; $i++) {
      286         476  
    4119 260         204 my $entry = $loXstack{toc}->[$i];
    4120 260         221 my $macro = $entry->{macro};
    4121 260 100       308 if ($opts->{mini}) {
    4122 86 100 100     290 last if $macro eq $mini_macro or $macro eq "Pt";
    4123             }
    4124 246 100       296 if ($opts->{summary}) {
    4125 110 50 66     219 if ($opts->{mini} and $mini_macro eq "Ch") {
    4126 0 0       0 next unless $macro eq "Sh";
    4127             }
    4128             else {
    4129 110 100       228 next unless $macro =~ /^(?:Pt|Ch)$/;
    4130             }
    4131             }
    4132 181         193 my $title_level = header_level($macro);
    4133              
    4134             # Computation of $level and $previous_title_level
    4135 181 100       324 if ($level == 0) {
        100          
        100          
        50          
    4136 40         30 $level = 1;
    4137 40         30 $previous_title_level = $title_level;
    4138             }
    4139             elsif ($title_level > $previous_title_level) {
    4140 75         68 my $diference = $title_level - $previous_title_level;
    4141 75 100       100 if ($type eq "xhtml") {
        100          
    4142 53         78 print " " x ($level + 1), "
      \n";
    4143             }
    4144             elsif ($type eq "nav") {
    4145 6         8 print " " x ($level + 1), "
      \n";
    4146             }
    4147 75         51 $previous_title_level = $title_level;
    4148 75         54 $level = $level + $diference;
    4149             }
    4150             elsif ($title_level < $previous_title_level) {
    4151 36         33 my $diference = $title_level - $previous_title_level;
    4152 36 100       49 $diference = 1 - $level if $diference + $level < 1;
    4153 36         63 print " " x ($level + 1), "$close_item\n";
    4154 36         60 for (my $i = $level ; $i > $level + $diference ; $i--) {
    4155 45         95 print " " x $i, "$close_list$close_item\n";
    4156             }
    4157 36         23 $previous_title_level = $title_level;
    4158 36         27 $level = $level + $diference;
    4159 36 50       54 $level = 1 if $level < 1;
    4160             }
    4161             elsif ($title_level == $previous_title_level) {
    4162 30         54 print " " x ($level + 1), "$close_item\n";
    4163             }
    4164              
    4165             # Print entry
    4166 181 100       266 if ($type eq "ncx") {
        100          
        50          
    4167 30         27 my $num = $entry->{num};
    4168 30 100       42 $num = "$num. " if $num;
    4169 30         48 print " " x ($level + 1), qq{\n};
    4170 30         60 print " " x ($level + 2),
    4171             qq{$num$entry->{title}\n};
    4172 30         24 my $href = $entry->{href};
    4173 30         54 print " " x ($level + 2), qq{\n};
    4174             }
    4175             elsif ($type eq "xhtml") {
    4176 140         157 xhtml_toc_like_entry($entry, $opts, $level);
    4177             }
    4178             elsif ($type eq "nav") {
    4179 11         11 my $href = $entry->{href};
    4180 11         7 my $num = $entry->{num};
    4181 11 100       15 $num = "$num. " if $num;
    4182 11         32 print " " x ($level + 1),
    4183             qq{
  • $num$entry->{title}\n};
  • 4184             }
    4185             }
    4186 40 50       110 print " " x ($level + 1), "$close_item\n" if $level > 0;
    4187 40         100 for (my $i = $level ; $i > 1 ; $i--) {
    4188 30         79 print " " x $i, "$close_list$close_item\n";
    4189             }
    4190              
    4191             # TOC bottom
    4192 40 100       88 if ($type eq "ncx") {
        100          
        50          
    4193 3         8 print "", "\n";
    4194             }
    4195             elsif ($type eq "xhtml") {
    4196 36         46 print " ", "\n";
    4197 36         145 print "", "\n";
    4198             }
    4199             elsif ($type eq "nav") {
    4200 1         2 print " ", "\n";
    4201 1         3 print "", "\n";
    4202             }
    4203             } # ]]]
    4204              
    4205             sub xhtml_toc_like_entry { # [[[
    4206 166     166 0 145 my ($entry, $opts, $level) = @_;
    4207 166         164 my $href = $entry->{href};
    4208 166         109 my $num = "";
    4209 166 100 66     398 unless ($opts->{nonum}
          66        
    4210             or ($href =~ /^index/ and not $Opts{all_in_one_file}))
    4211             {
    4212 118 100       121 if ($opts->{toc}) {
    4213 92         77 $num = $entry->{num};
    4214 92 100       133 $num .= ". " if $num;
    4215             }
    4216             else {
    4217 26         47 $num = "$entry->{count}. ";
    4218             }
    4219             }
    4220 166 100       183 if ($Opts{all_in_one_file}) {
    4221 76         267 print " " x ($level + 1),
    4222             qq{
  • $num$entry->{title}\n};
  • 4223             }
    4224             else {
    4225 90         307 print " " x ($level + 1),
    4226             qq{
  • $num$entry->{title}\n};
  • 4227             }
    4228             } # ]]]
    4229              
    4230             1;
    4231              
    4232             # vim:foldmarker=[[[,]]]:foldmethod=marker:sw=4:sts=4:expandtab