# -*-perl-*-
# $Id: xref.wrt 6242 2010-03-01 20:49:04Z mnodine $
# Copyright (C) 2002-2005 Freescale Semiconductor, Inc.
# Distributed under terms of the Perl license, which is the disjunction of
# the GNU General Public License (GPL) and the Artistic License.
# Writer for xref files
=pod
=begin reST
=begin Description
This writer exports cross reference targets defined in the source
reStructuredText (reST) file. The output is in reST format and
includes:
1. All non-anonymous internal targets, exported as ::
.. _<targetName>: <base-file><ext>.<suffix>#<targetID>
Here <base-file> is the base file name (file name without extension).
2. All citations, exported as::
.. _<citationName>: <base-file><ext>.<suffix>#<citationID>
3. Substitution definitions that have the same names as internal
targets but do not, directly or indirectly through substitution
references, define internal target or refer to external
references or footnotes.
4. Definition of substitutions referred to by the substitutions
exported according to 3. Substitution definitions are exported
as defined in the source file.
5. If -W xref-sections is specified,
a. Targets for all section titles, exported as ::
.. _<base-file>.<base-title>: <base-file><ext>.<suffix>#<titleID>
Here <base-title> is the title name prior to any autonumbering.
b. Substitution definitions for all section titles, exported as ::
.. |<base-file>.<base-title>| replace:: <section-title>
c. Target for the file itself, exported as ::
.. _<base-file>.: <sourceFileName><ext>.<suffix>
d. Substitution definition for the file itself, exported as ::
.. |<base-file>.| replace:: <document-title>
6. If -W xref-role-targets is specified, then inline targets that are
defined through an interpreted text target role are included in the
index.
This writer uses the following output definitions:
-W appendix=<string> String to use for an appendix reference when
xref-sections=1 (default "Appendix"). An
appendix is a document with a
"number-prefixed" title that is not a number.
-W chapter=<string> String to use for a chapter reference when
xref-sections=1 (default "Chapter"). A
chapter is a document with a
"number-prefixed" title that is a number.
-W file-suffix=<suffix> Specify a file suffix to be used for
exporting the cross reference targets
(default is html).
-W filename-ext=<ext> Specify an extension to the filename,
(e.g. "_main") so the file location of
targets becomes <file><ext>.<suffix>
(default is "").
-W file-path=<dir> Specify additional path information for
the target file (default is "")
-W section=<string> String to use for a section reference when
xref-sections=1 (default "Section").
-W sprintf=<string> Specify an sprintf string for formatting the
output definitions (default is "%s").
-W xref-role-target[=<name>]
Output cross-references for non-empty
internal targets created from interpreted
text roles (default is 0, or if specified
with no name, 'target').
-W xref-sections=<0|1> Output cross-reference for section titles
(default is 0).
-W xref-targets=<0|1> Output cross-reference for internal targets
(default is 1)
=end Description
=end reST
=cut
sub BEGIN = {
# My -W flags
use vars qw($appendix $chapter $file_suffix $filename_ext $file_path
$section $sprintf $xref_role_target
$xref_sections $xref_targets);
# Run-time globals
use vars qw($BASE_FILE $OUT_FILE %TARGET_NAMES @TARGETS $XREF_STRS);
# Defaults for -W flags
$appendix = 'Appendix' unless defined $appendix;
$chapter = 'Chapter' unless defined $chapter;
$file_suffix = 'html' unless defined $file_suffix;
$filename_ext = '' unless defined $filename_ext;
$file_path = '' unless defined $file_path;
$section = 'Section' unless defined $section;
$sprintf = "%s" unless defined $sprintf;
$xref_role_target = 0 unless defined $xref_role_target;
$xref_role_target = 'target' if ref $xref_role_target eq 'SCALAR';
$xref_sections = 0 unless defined $xref_sections;
$xref_targets = 1 unless defined $xref_targets;
$file_path .= "/" if ($file_path && $file_path !~ /\/$/);
}
sub exportableDef {
my ($dom) = @_;
# uncoverable branch true note:It will probably never be defined
return $dom->{_xref}{exportable}
if defined $dom->{_xref}{exportable};
my @contents = $dom->contents();
$dom->{_xref}{exportable} = "true";
foreach my $child (@contents) {
my $ctag = $child->tag;
if ($ctag eq 'target' ||
$ctag eq 'footnote_reference' ||
$ctag eq 'problematic' ||
($ctag eq 'reference' && $child->{attr}{refuri})) {
$dom->{_xref}{exportable} = "false";
}
}
return $dom->{_xref}{exportable};
}
# During the first pass, write all non-anonymous internal targets and
# citations out.
phase PREPROCESS {
sub document = {
my ($dom, $str) = @_;
$OUT_FILE = $BASE_FILE = $dom->{attr}{source};
$BASE_FILE =~ s/\..*?$//;
$OUT_FILE .= "$filename_ext.$file_suffix"
if (!($OUT_FILE =~ s/\..*?$/$filename_ext\.$file_suffix/o));
return unless $xref_targets;
my $str2;
my ($key, $value);
foreach (@TARGETS) {
my ($name, $id) = @$_;
$str2 .= sprintf "$sprintf\n", ".. _`$name`: $file_path$OUT_FILE#$id";
}
return $str2;
}
# Save all citations in @TARGETS array. The elements of the array
# are references to two-element (name, refid) pairs.
sub citation|paragraph = {
my ($dom, $str) = @_;
my @names = grep(!$TARGET_NAMES{$_},
@{$dom->{attr}{names}}) if $dom->{attr}{names};
# Note: the following is faking a method call with a null obj.
# It's safe because this method should not require its object
# in this context.
push @TARGETS, map
[$_, Text::Restructured::NormalizeId(0, $_)], @names;
@TARGET_NAMES{@names} = (1) x @names;
return;
}
sub section = {
my ($dom, $str) = @_;
# Note; the first name in {attr}{names} is the section name itself;
# any succeeding elements were attached by targets directed to the
# section.
# uncoverable branch false note:Expect always to have names
my @names = @{$dom->{attr}{names}} if $dom->{attr}{names};
shift @names;
@names = grep(!$TARGET_NAMES{$_}, @names);
push @TARGETS, map
[$_, Text::Restructured::NormalizeId(0, $_)], @names;
@TARGET_NAMES{@names} = (1) x @names;
return;
}
# Save non-anonymous internal targets into @TARGETS array. The
# elements of the array are references to two-element (name, id)
# pairs (for direct targets) or (name, refid) pairs (for indirect
# targets).
sub target = {
my ($dom, $str) = @_;
my $attr = $dom->{attr};
if (!$attr->{refuri} && !$attr->{anonymous} &&
(!$attr->{role} || $attr->{role} eq $xref_role_target)) {
$attr->{names} = $attr->{dupnames}
if defined $attr->{dupnames} && !defined $attr->{names};
my @names = grep(!$TARGET_NAMES{$_},
@{$dom->{attr}{names}}) if $dom->{attr}{names};
my $refid = $attr->{refid};
# Devel::Cover condition 0 2 NormalizeId is always true
# uncoverable condition false note:NormalizeId is always true
push @TARGETS, map
[$_, $refid || Text::Restructured::NormalizeId(0, $_)], @names;
@TARGET_NAMES{@names} = (1) x @names;
}
return;
}
}
# During the second pass, handle substitution definitions. Export a
# definition if both of these conditions are true.
# o. It has the same name as an internal target or citation, or is
# referred to (directly or indirectly) by another definition that
# has the same name as an internal target.
# o. It does not contain (directly or indirectly) footnote and external
# references, internal target, or problematic elements.
# Also, handle xref-sections=1
phase PROCESS {
sub document = {
my ($dom, $str) = @_;
if ($xref_sections) {
$XREF_STRS .= ".. _$BASE_FILE.: $file_path$OUT_FILE\n";
my $title = '';
$dom->first->Recurse(sub {
my ($dom) = @_;
$title .= $dom->{text} if $dom->tag eq '#PCDATA';
}) if $dom->first->tag eq 'title';
$title = $dom->{attr}{title} || '' unless $title;
$title =~ s/^(.*?)\.?[\xa0\xc2]+/ /;
my ($number) = ($1);
my $ref = ! defined $number ? $title :
$number =~ /\d$/ ? "$chapter $number" : "$appendix $number";
$XREF_STRS .= ".. |$BASE_FILE.| replace:: $ref\n";
}
return $XREF_STRS;
}
sub section = {
my ($dom, $str) = @_;
if ($xref_sections) {
my $name = $dom->{attr}{names}[0];
return if ! defined $name;
$XREF_STRS .= (".. _$BASE_FILE.$name: $file_path$OUT_FILE#" .
Text::Restructured::NormalizeId(0, $name) . "\n");
my $title = '';
$dom->first->Recurse(sub {
my ($dom) = @_;
$title .= $dom->{text} if $dom->tag eq '#PCDATA';
});
$title =~ s/^((.*?)(\..*?)?)[\xa0\xc2]+/ /;
my ($number, $prefix, $suffix) = ($1, $2, $3);
my $ref = ! defined $number ? $title :
defined $suffix ? "$section $number" :
$prefix =~ /\d$/ ?"$chapter $number" : "$appendix $number";
$XREF_STRS .= ".. |$BASE_FILE.$name| replace:: $ref\n";
}
}
sub substitution_definition = {
my ($dom, $str) = @_;
# Do nothing if this definition has been exported, has been
# previously identified as not exportable, or does not have
# the same name as an internal target or citation.
return unless $xref_targets;
# Devel::Cover +2 condition 0 1 null string always false
return
if ($dom->{_xref}{exported} ||
($dom->{_xref}{exportable} || '') eq "false" ||
!defined $TARGET_NAMES{lc $dom->{attr}{names}[0]});
return if (exportableDef($dom) eq "false");
$XREF_STRS .= $dom->{lit} . "\n";
$dom->{_xref}{exported} = 1;
return;
}
}