Author: amc
Date: Sat Aug  4 16:54:00 2012
New Revision: 1369371

URL: http://svn.apache.org/viewvc?rev=1369371&view=rev
Log:
Doxygen linkage - alpha version

Added:
    trafficserver/site/trunk/lib/DoxygenSymbol.pm   (with props)
    trafficserver/site/trunk/lib/WikiVar.pm
    trafficserver/site/trunk/lib/doxy-link.pm
    trafficserver/site/trunk/lib/doxy-sym-generate.pl   (with props)
Modified:
    trafficserver/site/trunk/content/docs/trunk/sdk/index.en.mdtext
    
trafficserver/site/trunk/content/docs/trunk/sdk/io-guide/net-vconnections.en.mdtext
    trafficserver/site/trunk/lib/view.pm

Modified: trafficserver/site/trunk/content/docs/trunk/sdk/index.en.mdtext
URL: 
http://svn.apache.org/viewvc/trafficserver/site/trunk/content/docs/trunk/sdk/index.en.mdtext?rev=1369371&r1=1369370&r2=1369371&view=diff
==============================================================================
--- trafficserver/site/trunk/content/docs/trunk/sdk/index.en.mdtext (original)
+++ trafficserver/site/trunk/content/docs/trunk/sdk/index.en.mdtext Sat Aug  4 
16:54:00 2012
@@ -35,3 +35,4 @@ sections may refer to functionality that
 
 If you find any such issues, you may want to submit a [bug or a 
patch](https://issues.apache.org/jira/secure/CreateIssue!default.jspa?pid=12310963).
 Or find out how to [find  bugs](), [create useful bug reports]() or 
[patches]().
+

Modified: 
trafficserver/site/trunk/content/docs/trunk/sdk/io-guide/net-vconnections.en.mdtext
URL: 
http://svn.apache.org/viewvc/trafficserver/site/trunk/content/docs/trunk/sdk/io-guide/net-vconnections.en.mdtext?rev=1369371&r1=1369370&r2=1369371&view=diff
==============================================================================
--- 
trafficserver/site/trunk/content/docs/trunk/sdk/io-guide/net-vconnections.en.mdtext
 (original)
+++ 
trafficserver/site/trunk/content/docs/trunk/sdk/io-guide/net-vconnections.en.mdtext
 Sat Aug  4 16:54:00 2012
@@ -25,7 +25,7 @@ about the Traffic Server abstraction for
 
 The netvconnection functions are listed below:
 
-* [TSNetAccept](link/to/doxygen)
-* [TSNetConnect](link/to/doxygen)
+* [dox 'TSNetAccept'] in [dox "TSNetAccept" :src_file]
+* [dox %TSNetConnect%] in [dox <TSNetConnect> :src_file]
 
 

Added: trafficserver/site/trunk/lib/DoxygenSymbol.pm
URL: 
http://svn.apache.org/viewvc/trafficserver/site/trunk/lib/DoxygenSymbol.pm?rev=1369371&view=auto
==============================================================================
--- trafficserver/site/trunk/lib/DoxygenSymbol.pm (added)
+++ trafficserver/site/trunk/lib/DoxygenSymbol.pm Sat Aug  4 16:54:00 2012
@@ -0,0 +1,21 @@
+use Class::Struct DoxygenSymbol =>
+  [ name => '$' # symbol name
+  , id => '$' # Doxygen assigned ID
+  , text => '$' # Descriptive text
+  , src_idx => '$' # index of source file in global array
+  , arg_text => '$' # argument signature text, if any
+  ]
+;
+
+package doxy;
+
+sub unmangle_file_name {
+    my $file = shift;
+    $file =~ s/[.]html$//;
+    $file =~ s/_1/:/g;
+    $file =~ s/_8/./g;
+    $file =~ s/__/_/g;
+    return $file;
+}
+
+1;
\ No newline at end of file

Propchange: trafficserver/site/trunk/lib/DoxygenSymbol.pm
------------------------------------------------------------------------------
    svn:executable = *

Added: trafficserver/site/trunk/lib/WikiVar.pm
URL: 
http://svn.apache.org/viewvc/trafficserver/site/trunk/lib/WikiVar.pm?rev=1369371&view=auto
==============================================================================
--- trafficserver/site/trunk/lib/WikiVar.pm (added)
+++ trafficserver/site/trunk/lib/WikiVar.pm Sat Aug  4 16:54:00 2012
@@ -0,0 +1,1026 @@
+#
+# WikiVar  -  A class for doing Wiki style variable processing on text.
+# by Alan M. Carroll
+# http://thought-mesh.net
+#
+# Version 1.2.1
+# 04 Dec 2008
+#
+# See the readme or POD for details, installation instructions, and
+# license information.
+#
+# Copyright (c) 2008-2010, Network Geographics, Inc.
+#
+# -----------------------------------------------------------------------------
+use strict;
+# -----------------------------------------------------------------------------
+package Text::WikiVar::CodeRef;
+# This is a specialized class for handling references to subroutines.
+# Bad things happen with AUTOLOAD if we try to capture a CODE reference before
+# the symbol is defined. As a result, we need to pass in an object that has
+# the name and can cache a CODE pointer so that we can lazy eval the CODE
+# reference and potentially pull in the supporting package to avoid AUTOLOAD
+# entirely.
+
+# Two members, the fully qualified name and the CODE reference
+use constant NAME_IDX => 0;
+use constant CODE_IDX => 1;
+
+# First argument is fully qualified name.
+# Optional second is a CODE pointer. This is useful for closures
+sub new {
+    bless [ $_[1], $_[2]], $_[0];
+}
+
+sub name {
+    my ($self, $value) = @_;
+    return defined($value) ? ($self->[NAME_IDX] = $value) : $_[0]->[NAME_IDX];
+}
+
+# Get a CODE pointer from the CodeRef.
+# Returns a CODE object if it found the subroutine, or a string with
+# an error condition if not.
+sub getCode {
+    my ($self) = @_;
+    my $status;
+
+    if (not $self->[CODE_IDX]) { # no cached pointer
+        no strict 'refs';
+        # see if it's there already
+        my $name = $self->[NAME_IDX];
+        $self->[CODE_IDX] = *{$name}{CODE};
+        if (not $self->[CODE_IDX]) {
+            # not there, try to bring in the package
+            $self->[NAME_IDX] =~ m!(.*)::(?:[^:]+)$!o;
+            my $p = $1; # package name
+            eval "require $p;";
+            if ($@) {
+                $status = 'Defun Error: Failed to load package "' . $p
+                        . '" for subroutine "' . $name
+                        . '" because "' . $@ . '"';
+            } else {
+                $self->[CODE_IDX] = *{$name}{CODE};
+                if (not $self->[CODE_IDX]) {
+                    $status = 'Defun Error: Undefined subroutine "'.$name.'"';
+                }
+            }
+        }
+    }
+    return $status ? $status : $self->[CODE_IDX];
+}
+# -----------------------------------------------------------------------------
+package Text::WikiVar::Arguments;
+# Struct for holding the arguments to a WikiVar function.
+# It can be used as an array reference to access required arguments, with
+# argv[0] being the WikiWord and argv[1]..argv[n] the arguments supplied in
+# the text. argc() returns the number of positional arguments.
+# The structure can be dereferenced as a hash to access named and keyword
+# arguments. Keyword arguments always have a value of '1' if present, undefined
+# if not. 
+#
+# Funky implementation notes
+# We use an array as the base implementation type but we can't use that
+# directly because we want to override subscripting for clients. The actual
+# blessed object is a reference to a reference to the array payload. We can
+# then use scalar dereference in methods to get to the array payload,
+# bypassing the subscript overload.
+
+# Class array indices
+use constant POSITIONAL => 0;   # Positional arguments
+use constant NAMED => 1;        # Named and keyword arguments
+use constant REQUIRED_ARGS => 2; # Arguments 1..N only as an array (cache)
+use constant SUPPLIED_ARGS => 3; # All supplied arguments as an array (cache)
+
+use overload '%{}' => sub { return ${$_[0]}->[NAMED]; },
+             '@{}' => sub { return ${$_[0]}->[POSITIONAL]; }
+             ;
+
+sub new {
+    my $class = shift;
+    $class = ref($class) || $class; # force to string (name of class)
+    my ($word, $required, $named) = @_;
+    my $payload = [ [ $word, @$required ] , $named ];
+    my $self = \$payload;
+    bless $self, $class;
+    my $debug = ref $self;
+    my $debug2 = ref $$self;
+    return $self;
+}
+
+# Required argument value
+sub argv {
+    my ($self, $n) = @_;
+    return $self->[$n];
+}
+
+# Number of positional arguments
+sub argc { return scalar ${$_[0]}->[POSITIONAL]; }
+
+# Value for named argument.
+sub value {
+    my ($self, $name) = @_;
+    return $self->{$name};
+}
+
+# WikiWord invoked.
+sub word { return $_[0]->[0]; }
+
+# For each name in the arguments, force that named argument (if present)
+# to be an array.
+sub force_array {
+    my $payload = ${shift()};
+    my $args = $payload->[NAMED];
+    for my $key (@_) {
+        $args->{$key} = [$args->{$key}] if exists $args->{$key} and ref 
$args->{$key} ne 'ARRAY';
+    }
+}
+
+# Return a list of the required arguments without the WikiWord.
+sub required_args {
+    my $payload = ${shift()};
+    if (not defined $payload->[REQUIRED_ARGS]) {
+        # fault in the positional args less arg[0]
+        my $tmp = $payload->[POSITIONAL];
+        shift @$tmp;
+        $payload->[REQUIRED_ARGS] = $tmp;
+    }
+    return $payload->[REQUIRED_ARGS];
+}
+# -----------------------------------------------------------------------------
+package Text::WikiVar;
+our $VERSION = "1.2.3";
+
+use constant TRUE => 1;
+use constant FALSE => 0;
+
+# A bit ugly, but Class::Struct, fields, and Class::Struct::Fields don't do 
what
+# I really need done, which is default values.
+
+BEGIN {
+    # Field for a WikiVar instance.
+    our %_FIELDS = ( DELIMITER => { default => '$' }        # Character that 
marks WikiVar expressions
+                   , KEYWORD_MARK => { default => '' }      # Character that 
marks a keyword argument to a function.
+                   , PRE_ERROR_TEXT => { default => '<!-- WikiVar Error: ' }   
 # Lead text for error output
+                   , POST_ERROR_TEXT => { default => ' -->' }   # Trail text 
for error output
+                   , VALUE_RXP => { private => 1 }            # A value
+                   , ARG_RXP => { private => 1}           # An argument to a 
function
+                   , ARG_PARSE_RXP => { private => 1 }     # Pick apart an 
argument for handling
+                   , INVOCATION_RXP => { private => 1 }    # A WikiVar 
invocation
+                   , SCOPE => { default => [] , private => 1 }             # 
Stack of scopes
+                   , TABLE => { default => {} , private => 1 } # Symbol table
+                   , FALLBACK => { }                        # Fallback to call 
when a WikiVar name is not found.
+                   );
+
+    # I originally used the Enum package, but that's not installed by default
+    # and it only saves me one line of code (I have to walk over the fields
+    # anyway, to get the index data correct). Also, Enum uses closures instead
+    # of eval, so I am not sure its values get optimized by the compiler.
+    no strict "refs";
+    my $idx = 0;
+    foreach my $key (sort(keys(%_FIELDS))) {
+        $_FIELDS{$key}{index} = $idx;
+        *{"$key"} = eval "sub () { $idx }"; # use eval to force the constant, 
avoid a closure
+        ++$idx;
+    }
+    use strict "refs";
+}
+our %_FIELDS; # Declaration from BEGIN doesn't propagate, so we have to 
declare again (it's still the same variable)
+# Special name used to scope fallback assignments
+use constant FALLBACK_NAME => '*Fallback';
+
+## Class constants
+# The format for a name, either a function or an argument
+our $NAME_RXP = qr/[_[:alpha:]](?:-?[_[:alpha:][:digit:]])*/o;
+# Base set of symmetric quote marks accepted
+our $QUOTE_MARKS = q('"@`~!#%^&*=+;:/\\?*|$);
+# Table of supported assymmetric quotes
+our %QUOTE_TABLE = ( '[' => ']' , '(' => ')' , '{' => '}' , '<' => '>' );
+# Asymmetric quoted value RE
+#our $PAREN_VALUE_RXP = qr/\([^)]*\)|<[^>]*>|\[[^]]*\]|{[^}]*}/o;
+# ---------------------------------------------------------------------------
+sub new {
+    my $class = shift;
+    $class = ref($class) || $class; # force to string (name of class)
+    my $self = [];
+    bless $self, $class;
+
+    # Convert argument keys to upper case.
+    my $flip = FALSE;
+    my %args = map { $flip = not $flip; $flip ? uc : $_; } @_;
+
+    # Initial values. Use argument if present, otherwise the default if 
available
+    while (my ($key, $value) = each %_FIELDS) {
+        my $idx = $value->{index};
+        if (not $value->{private} and exists $args{$key}) {
+            $self->[$idx] = $args{$key};
+        } elsif (exists $value->{default}) {
+            $self->[$idx] = $value->{default};
+        }
+    }
+
+    # Build up the regular expressions we need.
+    # We do many of these in two steps because we don't do the string 
construction
+    # in the same line that we compile it to a RE.
+    my $marks = $QUOTE_MARKS;
+    my $dl = $self->[DELIMITER]; # non-RE base delimiter.
+    my $dl_right;
+    my $dl_left = quotemeta($dl);
+    if (exists $QUOTE_TABLE{$dl}) {
+        $dl_right = quotemeta($QUOTE_TABLE{$dl});
+    } else {
+        $dl_right = $dl_left;
+    }
+    # Clip delimiter from any quote matching
+    $marks =~ s/$dl_left//;
+    delete $QUOTE_TABLE{$dl};
+    # Clip out the keyword mark if set (only symmetric quotes supported)
+    my $kw = $self->[KEYWORD_MARK];
+    if ($kw) {
+        $kw = quotemeta($kw);
+        $marks =~ s/$kw//;
+        $kw .= $NAME_RXP;
+    } else {
+        $kw = ''; # force to null string so $kw is always a string after this
+    }
+    # RE that matches a value quoted by any of the allowed symmetric quote 
marks
+    my $quoted_value_rxp = join('|', map { my $c = quotemeta($_); 
$c.'[^'.$c.']*'.$c} split(//,$marks));
+    # Generate an RE for the assymmetric quotes
+    my @paren_rxp;
+    while (my ($left, $right) = each %QUOTE_TABLE) {
+        push(@paren_rxp, "[$left][^$left]*[$right]");
+    }
+    my $paren_rxp = join('|', @paren_rxp);
+    $paren_rxp = qr/$paren_rxp/;
+    # Any quoted value, symmetric or asymmetric
+    $self->[VALUE_RXP] = qr<$quoted_value_rxp|$paren_rxp>;
+    # An argument to an invocation, syntax checking version
+    my $text = "(?:$NAME_RXP\\s*)?" . $self->[VALUE_RXP];
+    $text = '(?:' . $text . ')|(?:' . $kw . ')' if $kw;
+    $self->[ARG_RXP] = qr<$text>;
+    # An argument to an invocation, processing version
+    $text = "(?:($NAME_RXP)\\s*)?(" . $self->[VALUE_RXP] . ')';
+    $text = '(?:' . $text . ')|(' . $kw . ')' if $kw;
+    $self->[ARG_PARSE_RXP] = qr<$text>;
+    # If the delimiter is symmetric we want to match double instances and 
convert them to a single instance
+    # as a convenience. We do that by marking the content with a 0,1 marker 
and checking for it being empty
+    # during processing. We do not do this if the delimiter is assymmetric.
+    $text = $dl_left . '(?:' . '(' . $NAME_RXP . ')(?:(=' . $self->[VALUE_RXP] 
. ')|((?:\s*' . $self->[ARG_RXP] . ')*)))';
+    $text .= '?' if $dl_left eq $dl_right;
+    $text .= $dl_right;
+    $self->[INVOCATION_RXP] = qr<$text>;
+
+    return $self;
+}
+
+# Now we can create a static instance for convenience.
+our $STATIC_INSTANCE = __PACKAGE__->new();
+
+# ---------------------------------------------------------------------------
+# Self extraction for class methods.
+# Handle three cases:
+# - Called directly as a subroutine (Text::WikiVar::method)
+# - Called as class method (Text::WikiVar->method)
+# - Called from instance ($instance->method)
+# An appropriate value for $self is returned, and removed from the argument
+# list if present.
+sub _self {
+    my $self = $_[0][0]; # Get the first argument
+    if (not ref $self) { # not a reference, need to fix it up
+        shift @{$_[0]} if $self eq __PACKAGE__; # drop only if it's the package
+        $self = $STATIC_INSTANCE; # use static instance
+    } else {
+        shift @{$_[0]}; # was an instance, so drop it
+    }
+    return $self;
+}
+# ---------------------------------------------------------------------------
+sub assign ($$$) {
+    my $self = _self(\@_);
+    my ($name, $value) = @_;
+
+    my $table = $self->[TABLE]; # should be hash ref
+    my $scope = $self->[SCOPE]; # stack of hash refs
+    my $status = '';
+
+    if ($name =~ m/^$NAME_RXP$/) { # verify legal name
+        # fault in the definition hash if it's not there.
+        if (ref $table ne 'HASH') {
+            $table = {};
+            $self->[TABLE] = $table;
+        }
+
+        # Store the old value so we can unwind this scope later.
+        # Don't save the value if it's already there. That means it's
+        # already been changed in this scope.
+        # Note that if it wasn't defined already then we'll store undef in the
+        # scope table. This indicates that we need to delete the value when
+        # the scope is exited.
+        # Note that scope is a stack with the innermost scope at
+        # index 0.
+        $scope->[0]->{$name} = $table->{$name} if $scope and not exists 
$scope->[0]->{$name};
+
+        if (not defined($value)) {
+            delete $table->{$name}; # assigning undef deletes assignment
+        } else {
+            # be backwards compatible for now
+            $value = new Text::WikiVar::CodeRef('*',$value) if ref $value eq 
'CODE';
+
+            if (! ref $value || $value->isa('Text::WikiVar::CodeRef')) {
+                $table->{$name} = $value;
+            } else { # not a valid value
+                $status = 'Bad value type - "' . ref($value)
+                        . '" - in WikiVar assignment to variable "' . $name . 
'"';
+            }
+        }
+    } else {
+        $status = 'Invalid name for function: "' . $name . '"';
+    }
+    return $status ? ( $self->[PRE_ERROR_TEXT] . $status . 
$self->[POST_ERROR_TEXT] ) : '';
+}
+
+sub fallback {
+    my $self = _self(\@_);
+    if (scalar @_){
+        my $value = shift;
+        my $scope = $self->[SCOPE];
+        $scope->[0]->{FALLBACK_NAME()} = $self->[FALLBACK] if $scope and not 
exists $scope->[0]->{FALLBACK_NAME()};
+        if (ref $value eq 'CODE') {
+            $self->[FALLBACK] = new 
Text::WikiVar::CodeRef(FALLBACK_NAME,$value);
+        } elsif (not ref $value) {
+            $self->[FALLBACK] = $value;
+        } elsif (value->isa('Text::WikiVar::CodeRef')) {
+            $value->name(FALLBACK_NAME);
+            $self->[FALLBACK] = $value;
+        } else {
+            warn "Fallback not set because value was an inappropriate type.\n";
+        }
+    } else {
+        return $self->[FALLBACK];
+    }
+}
+
+sub _invoke {
+    my ($name, $func)  = (shift,shift);
+    my $debug = $_[0];
+    my $debug2 = ref $debug;
+    my $output = '';
+    my $errText;
+    if (not ref $func) { # simple text, just use it
+        $output = $func;
+    } elsif ($func->isa('Text::WikiVar::CodeRef')) { # something to call
+        my $code = $func->getCode();
+        if (ref $code eq 'CODE') { # found the code
+            $output = eval { &$code(@_); }; # wrap in eval to trap exceptions
+            $errText = 'Error: Invocation of "' . $name
+                     . '" as defun "' . $func->name
+                     . '" failed because "' . $@ . '"'
+                     if $@; # failure during invocation
+        } else { # something went wrong, use as error text
+            $errText = "Error: CodeRef for '$name' has bad type: " . 
ref($code);
+        }
+    } else { # something went wrong, use as error text
+        $errText = "Error: WikiVar '$name' has bad type: " . ref($func);
+    }
+    return ( $errText, $output );
+}
+
+# Invoke a macro with arguments.
+# Parameters:
+#    Macro Name
+#    Argument structure
+sub invoke {
+    my $self = _self(\@_);
+    my $name = shift;
+    my $output = '';
+    my $errText = '';
+    my $macro;
+
+    # get our table out of the context
+    my $table = $self->[TABLE];
+
+    if (ref $table eq 'HASH' && defined ($macro = $table->{$name})) {
+        ($errText, $output) = _invoke($name, $macro, @_);
+    } elsif ($self->[FALLBACK]) {
+        ($errText, $output) = _invoke($name, $self->[FALLBACK], @_);
+    } else {
+        $errText = 'Error: WikiVar "' . $name . '" not defined' if not exists 
$table->{$name};
+    }
+    $errText = $self->[PRE_ERROR_TEXT] . $errText . $self->[POST_ERROR_TEXT] 
if $errText;
+    return wantarray() ? ($output , $errText) : ($errText ? $errText : 
$output);
+}
+
+# This sets up a new scope which saves old values of macros. These values
+# are restored by a LeaveScope call.
+sub enter_scope {
+    my $self = _self(\@_);
+    my $scope = $self->[SCOPE];
+    if (ref $scope ne 'ARRAY') {
+        $self->[SCOPE] = [ {} ]; # seed original array of single hash
+    } else {
+        unshift @$scope, {}; # push an empty hash to save changes in scope
+    }
+}
+
+# Restore saved values for macros.
+sub leave_scope {
+    my $self = _self(\@_);
+    my $scope = $self->[SCOPE];
+    if ($scope) { # is there a scope to leave?
+        my $table = $self->[TABLE];
+        my $values = shift @$scope;
+        # For each value in the scope, if it's set restore $table
+        # to that otherwise delete the element from table
+        foreach my $key (keys %$values) {
+            my $v = $values->{$key};
+            if ($v) {
+                $table->{$key} = $v;
+            } else {
+                delete $table->{$key};
+            }
+        }
+    }
+}
+
+# Takes an argument string and returns an argument object.
+sub string_to_args {
+    my $self = _self(\@_);
+    my ($word, $text, $flags) = @_;
+    my @required;
+    my %named;
+    my $rxp = $self->[ARG_PARSE_RXP];
+
+    while ($text =~ /$rxp/g) {
+        my $name = $1; # save these before they get scrozzled
+        my $value = $2;
+        my $keyword = $3;
+
+        if ($keyword){
+            $named{$keyword} = 1;
+        } else {
+            $value = substr($2, 1, length($2)-2); # strip quotes
+            $value = $self->process($value, $flags);
+            if ($name) {
+                if (exists $named{$name}) {
+                    if (ref $named{$name} eq 'ARRAY') { push(@{$named{$name}}, 
$value); }
+                    else { $named{$name} = [ $named{$name} , $value ]; }
+                } else {
+                    $named{$name} = $value;
+                }
+            } else {
+                push(@required, $value);
+            }
+        }
+    }
+    return Text::WikiVar::Arguments->new( $word, \@required , \%named );
+}
+
+# Perform text substitution for a single invocation match
+sub _process {
+    my ($self, $flags, $name, $assign_text, $arg_text) = @_;
+    my $zret = '';
+
+    if (not $name) {
+        # Matched empty content -> it's a double delimiter.
+        # Generate a single delimiter as the result.
+        $zret = $self->[DELIMITER];
+    } elsif ($assign_text){
+        my $value = '';
+        $value = $self->process(substr($assign_text, 2, length($assign_text) - 
3), $flags) if length $assign_text > 2;
+        $self->assign($name, $value);
+    } else { # if it's not an assignment, it's a call
+        $arg_text = '' unless defined($arg_text);
+        $zret = $self->invoke($name, $self->string_to_args($name, $arg_text, 
$flags));
+    }
+    return $zret;
+}
+
+# Process the text, substituting the results for the invocations.
+# Parameters
+#    Text
+#    Flags
+#        scope - create a scope around this transform
+
+sub process {
+    my $self = _self(\@_);
+    my ($zret, $flags) = @_;
+    my $rxp = $self->[INVOCATION_RXP];
+    $flags = {} if ref $flags ne 'HASH'; # force hash ref
+
+    my $inScope = $flags->{scope};
+    delete $flags->{scope}; # don't pass this down via recursion
+    $self->enter_scope() if $inScope;
+
+    $zret =~ s/$rxp/$self->_process($flags, $1, $2, $3)/eg;
+
+    if ($inScope) {
+        $self->leave_scope();
+        $flags->{scope} = $inScope; # restore original state of flags
+    }
+
+    return $zret;
+}
+
+sub error_bounding_text {
+    my $self = _self(\@_);
+    return ($self->[PRE_ERROR_TEXT], $self->[POST_ERROR_TEXT]);
+}
+
+# Return our version
+sub Version {
+    return $VERSION;
+}
+
+1;
+__END__
+
+
+=pod
+
+=head1 Name
+
+B<WikiVar>
+
+=head1 Synopsis
+
+WikiVar is a text formatter that provides roughly the same functionality as 
both
+Wiki variables and macros. That is, both simple text substitution and code
+invocation. Text substitutions (macros) can be defined programmatically or
+inline. Code invocations ("functions") can only be defined programmatically but
+used inline (thereby providing a mechanism for calling pre-existing Perl
+subroutines from text input).
+
+Stylistically WikiVar is based on Wiki variables but extends the syntax to
+provide better function invocation capabilities. It supports both positional,
+named, and keyword arguments with robust quoting mechanisms.
+
+=head1 Description
+
+=head2 Background
+
+WikiVar was originally intended as a module for an actual Wiki to improve
+variable handling, which for that Wiki code was scattered among numerous 
regular
+expressions in many modules, leading to a disturbing lack of consistency.
+Instead of that, WikiVar would provide a single place for registering all Wiki
+functions and, because it was easy and consistent to add, macros. Bad
+interactions between the variables would be minimized because they would all be
+processed in one pass, not each in its own separate pass, and much faster for
+the same reason. That project was sidelined but I kept WikiVar because
+it was useful in other circumstances.
+
+=head2 Overview
+
+WikiVar supports two types of usage, "macros" and "functions".
+Stylistically these are very similar, but the implementation / API differ.
+
+Macros are simple text substitution. The invocation of a macro is replaced by
+the text for that macro. Macros can be defined inline or via the API. This 
makes
+them handy for predefined terms or local abbrevations.
+
+Functions are calls to Perl subroutines. Arguments (both positional and named)
+are passed to the subroutines. The invocation is replaced by the output
+of the subroutine. Functions must be defined via the API, they cannot be
+assigned inline (only invoked).
+
+=head2 Inline Usage
+
+Inline usage is use of WikiVar by character sequences in the processed text.
+
+=head3 Using a macro inline
+
+The macro "name" is invoked by the character sequence C<$name$>. This set of
+characters is replaced by the value of the macro. As a special case, two
+adjacent delimiters are converted to a single delimiter. That is, C<$$> is
+turned in to C<$>.
+
+=head3 Defining a macro inline
+
+To define the macro "name" to have the value "value", use the text
+C<$name=@value@$> where C<@> is a E<delimiter>. The delimiter can be any
+printable non-whitespace character that is not a valid macro name character
+except C<$>, C< > >, C< ) >, C< ] >, or C< } >. The terminal delimiter is the
+same as the initial one, except for C< < >, C< ( >, C< [ >, C < { >. In these
+cases the terminal delimiter is C< > >, C< ) >, C< ] >, or C< } > respectively.
+The value must not contain any instances of the delimiter. Because of the wide
+variety of delimiters that can be used, this is rarely a problem. Even when it
+is, there is a work around described after the following examples.
+
+Examples:
+
+=over 4
+
+C<$name="value"$>
+
+C<$name=(value)$>
+
+C<$name=|value|$>
+
+=back
+
+The name must start with a letter or an underscore, and can contain letters, 
underscores, numbers and dashes. It
+may not end with a dash.
+
+Note that no white space is allowed in the syntax. The variable delimiter C<$>
+must immediately precede the initial name character and immediately follow the
+terminal value delimiter. This avoids problems with the use of the variable
+delimiter in normal text.
+
+The actual text of the assignment (everything between the C<$> characters
+and those characters as well) is removed completely the output text.
+
+The value itself is evaluated so that any variables in the value are evaluated.
+This is intended to allow defining a macro in terms of one or more other 
macros.
+However, it does mean that you can define new macros in the value part of a 
macro
+definition. This nested define must use a different delimiter than the outer 
define,
+limiting the possible recursion depth. However, such use is generally more 
trouble
+than benefit and is mentioned only to warn you to no do it.
+
+There is no provision for escaping characters in the value. This is acceptable
+because of the ability to select from a larger number of delimiters. If that's
+not sufficient then another macro consisting of just the problematic character 
can be
+defined and used to put the delimiter in to the macro value.
+
+=over 4
+
+C<$dq='"'$> C<$name="A double quote $dq$"$>
+
+=back
+
+=head3 Using a function inline
+
+To invoke the function "func", use the text C<$func$>. This calls the Perl 
subroutine
+associated with the name "func" and replaces the text with the output of the 
subroutine.
+
+Arguments can be passed. Such arguments are of three types, positional, named,
+and keyword. In general, positional arguments are required and named / keyword
+arguments are optional, but this is by convention. Each argument value is
+delimited in the same way as macro text. Named arguments are indicated by
+putting the undelimited name in front of the argument value E<without
+intervening whitespace>. Keyword arguments are names with a leading keyword 
mark
+character if keywords have been enabled (keyword arguments are disabled by
+default). Named arguments have values, keyword arguments don't, and positional
+arguments are just values.
+
+Named and keyword arguments can be in any order, interspersed among the
+positional arguments in any fashion, but positional arguments are
+passed in the same order as they occur in the text. Named and keyword arguments
+are ignored for the purposes of positional arguments, so that the following are
+equivalent (presuming ':' has been set as the keyword marking character):
+
+=over 4
+
+C<$func (arg1) 'arg2' <arg3E<gt> name1(value1) name2%value2% :key1 :key2$>
+
+C<$func :key2 {arg1} name2<value2E<gt> "arg2" :key1 name1[value1] (arg3)$>
+
+=back
+
+Conventionally the positional arguments are placed first, followed by named
+arguments, then keyword arguments. This is simply to reduce confusion, however,
+and you can use a different system if that works better for you.
+
+=head2 Programmatic API
+
+WikiVar supports two interfaces, object oriented and functional. These work the
+same way except that the functional interface uses a single pre-allocated 
global
+instance while the object oriented styles lets the client create and control
+multiple instances. Both of these are useful in different circumstances. The
+primary advantage of the OO interface is that the different instances can be 
set
+to have different processing styles (e.g. different variable delimiters) or
+different sets of defined macros / functions.
+
+=head3 Text::WikiVar
+
+=head4 Method new
+
+Create a new instance of WikiVar. The argument list is treated as a hash with
+the following keys examined.
+
+=over 4
+
+=item DELIMITER
+
+The variable delimiter which must be a single printable
+non-alphanumeric/underscore/dash character. Default value is '$'. Setting this
+to '%' will make invocations look more like normal Wiki variables. Note that 
the
+value for this will be forbidden for use as a value delimiter (and if set to
+something other than '$', then '$' can be used for delimiting values).
+
+=item KEYWORD_MARK
+
+Character mark for keywords. This has the same limitations as
+B<DELIMITER> and is similarly removed from the pool of valid value delimiters.
+The default is the empty string which disables keyword arguments. If set,
+keyword arguments will be processed. Of course, this and C<DELIMITER> must be
+different characters.
+
+=item FALLBACK
+
+This is used when text contains what looks like a WikiVar invocation but the
+name is not defined. If this is a string, then that string is used as the value
+of the variable. If B<FALLBACK> is a C<CODE> reference then it is called as if
+the undefined name was a function and B<FALLBACK> the code for that function.
+Note that as with C<AUTOLOAD>, this function can change the set of defined
+variable names. If an undefined name is use and B<FALLBACK> is not set (the
+default) then an error message is generated.
+
+This can be changed later using the C<fallback> method.
+
+=item PRE_ERROR_TEXT, POST_ERROR_TEXT
+
+If an error occurs, an error message is
+generated and used as the value of the variable causing the error. These values
+are prepended and appended to that message. The default is C<E<lt>!-- WikiVar
+Error:> and C< --E<gt>> respectively, so that if used to process HTML the 
errors
+are findable but not visible.
+
+=back
+
+=head4 Method assign
+
+This defines a macro or a function. The arguments are
+
+=over 4
+
+=item name
+
+The name of the variable. This must consist only of alphanumeric, dash or
+underscore characters. The name must start with a letter or an underscore, and
+must not end with a dash.
+
+=item value
+
+The value of the variable. This should be one of
+
+=over 4
+
+=item *
+
+A scalar. The result is a macro and the printable version of the value is used 
as
+the substitution text for the macro.
+
+=item *
+
+A reference to a subroutine. The result is a function. When the function is
+invoked the subroutine is called with the arguments provided by the inline
+invocation as an instance of the C<Text::WikiVar::Arguments> class.
+
+=item *
+
+An instance of C<Text::WikiVar::CodeRef>. This allows a subroutine to be
+specified by name. The actual code reference is not accessed until the function
+is invoked. This is useful if there are initialization ordering problems and 
the
+module with the subroutine may not be loaded when C<assign> is called. If a
+subroutine is defined at the point of assignment, it is generally preferable to
+just pass a reference to it instead of using C<CodeRef>.
+
+=item *
+
+C<undef>. This will erase the definition of the variable.
+
+=back
+
+=back
+
+=head4 Method fallback
+
+This has the same semantics as C<assign> except that it stores the variable in 
a
+special slot in the WikiVar instance and invokes it whenever a WikiWord is used
+that is not defined. The default behavior in such a case is to generate an
+error message as the output text but if the C<fallback> is set then it is 
called
+instead and its return value used.
+
+=head4 Method invoke
+
+This performs the substitution for the variable. The return value is the
+substitution string for the variable. The arguments are
+
+=over 4
+
+=item name
+
+The name of the variable.
+
+=item arguments
+
+If I<name> is a not a function then I<arguments> is ignored and may be omitted.
+Otherwise the arguments are passed on to the subroutine as is. Since the
+function is likely to be expecting a C<Text::WikiVar::Arguments> object you may
+want to use the C<string_to_args> method.
+
+=back
+
+=head4 Method string_to_args
+
+Convert an argument I<string> to an instance C<Text::WikiVar::Arguments>. The
+string format is as described for inline invocation. The argument list can be
+passed directly to a subroutine or passed, along with a name, to C<invoke>. 
Note
+that argument values are processed for further WikiVar substitutions.
+
+=head4 process
+
+This transforms a string, performing all macro substitutions and updating the
+context for any new macro definitions. It returns the transformed string. It
+takes two arguments.
+
+=over 4
+
+=item text
+
+The text string to transform.
+
+=item flags
+
+A reference to a hash containing processing flags. The currently defined
+flags are
+
+=over 4
+
+=item scope
+
+If set to a C<true> value, a scope is entered before processing and
+left after processing, isolating any inline definitions in the text.
+
+=back
+
+=back
+
+=head4 Method enter_scope
+
+Creates a new scope for definitions. Any definitions made in this scope can be
+reverted with C<leave_scope>. This is handy when processing multiple chunks of
+user text so that inline definitions in one chunk do not propagate to other 
chunks.
+
+=head4 Method leave_scope
+
+Destroys the current definition scope. All definitions (both inline and via 
C<assign>)
+made in the scope are reverted to their state before the scope was entered.
+
+=head3 Text::WikiVar::CodeRef
+
+As noted previously, this class is useful when intialization ordering means 
that
+a subroutine to be used as a function may not be loaded when C<assign> is
+called. In that case, a C<CodeRef> wrapper can be used to delay access to the
+C<CODE> reference until the function is invoked during text processing.
+
+=head4 Method new
+
+=over 4
+
+=item *
+
+I<name> The fully qualified name of the subroutine.
+
+=back
+
+=head3 Text::WikiVar::Arguments
+
+An instance of this class is passed to the subroutine for a function. It
+contains all of the arguments supplied in the text that invoked the function. 
It
+can be treated as an array reference for positional arguments or a hash
+reference for named and keyword arguments. If C<$args> is an instance of this
+class passed to a function, then
+
+=over 4
+
+=item *
+
+C<$args-E<gt>[0]> is the name of the function.
+
+=item *
+
+C<$args-E<gt>[$n]> is the C<n>th positional argument.
+
+=item *
+
+C<$args-E<gt>{name}> The value for the named or keyword argument C<name>. The 
value
+for a keyword argument is a true value if the keyword was present, and a false
+value if not. The value for a named argument is C<undef> if the argument was 
not
+present, the value if it was. The C<exists> keyword can also be used to check 
if
+a named or keyword argument was present.
+
+=back
+
+An instance is also a class with various utility methods.
+
+=over 4
+
+=item *
+
+I<argc> Return the number of positional arguments.
+
+=item *
+
+I<argv> Identical to C<$args-E<gt>[]>. Gotta give my fellow C/C++ programmers 
some love.
+
+=item *
+
+I<value> The same as C<$args-E<gt>{}> except that the argument is not 
implicitly
+quoted. Useful when the argument name is in a variable and not a literal.
+
+=item *
+
+I<word> The WikiVar variable name invoked. Identical to C<$args-E<gt>[0]> but
+a bit clearer in context.
+
+=item *
+
+I<force_array> Force values for named arguments to be C<ARRAY> references. The
+arguments to I<force_array> are names that name named arguments. If the value
+for one of the names is already an C<ARRAY> reference it is unchanged. If it is
+not, it is converted to a reference to an C<ARRAY> of length 1.
+
+=item *
+
+I<required_args> Return a reference to an array of the positional arguments. 
The
+invoking word is not included. Cached so it's very low marginal cost to call
+multiple times.
+
+=back
+
+=head2 Example
+
+Here is some example text illustrating the basic usage.
+
+ $Bob='<a href="mailto:[email protected]";>Bob</a>'$
+ $BobHomePage=(<a href="http://nowhere.com/~bob";>Home Page</a>)$
+ $Bobco="Bob's Organic Software and Vegetables"$
+
+ I first met $Bob$ a about 10 years ago. You can check out his biography at his
+ $BobHomePage$. Shortly after I met him he started $Bobco$ to combine his 
talent
+ for software development with his wife's green thumb. $Bobco$ has been a 
rousing
+ success, their "free onion with every download" marketing scheme taking the
+ market by storm. I see a good future for $Bob$ and $Bobco$.
+
+After processing, this yields
+
+ I first met <a href="mailto:[email protected]";>Bob</a> a about 10 years ago. 
You
+ can check out his biography at his
+ <a href="http://nowhere.com/~bob";>Home Page</a>. Shortly after I met him he
+ started Bob's Organic Software and Vegetables to combine his talent for 
software
+ development with his wife's green thumb. Bob's Organic Software and Vegetables
+ has been a rousing success, their "free onion with every download" marketing
+ scheme taking the market by storm. I see a good future for <a
+ href="mailto:[email protected]";>Bob</a> and Bob's Organic Software and 
Vegetables.
+
+=head1 Version History
+
+    0.1: 10 Oct 2003
+    1.0: 19 Nov 2004
+    1.1 : 16 May 2008
+    1.2 : 26 Oct 2008
+    1.2.1 : 04 Dec 2008
+
+=head1 Author
+
+    Alan M. Carroll
+    https://network-geographics.com
+
+=head1 Additional Credits
+
+The original goal of this project was to extend Textile so that I could do
+inline text macros (particularly, predefined ones for links). Originally I had
+hacked the Textile source to define the macros, which made maintenance
+difficult. WikiVar was started to generalize that. Eventually most of that
+functionality was provided by Webiki, but I still use WikiVar for inline macros
+and certain Perl based functionality.
+
+While doing the design I started working with Wiki and decided to expand the
+functionality enough to handle Wiki variables as well. This was part of an 
effort
+to support Textile formatting in a Wiki. The base logic for handling Wiki
+variables was a complete hack, not generalized and scattered about the source
+code. WikiVar supports all of the required utility in one place, in an easily
+customized, more consistent, and more efficient way.
+
+=head1 Copyright and License
+
+    Copyright (c) 2008 Network Geographics Inc.
+    (https://network-geographics.com/)
+    All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+*   Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+
+*   Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+
+*   Neither the name "WikiVar" nor the names of its contributors may
+    be used to endorse or promote products derived from this software
+    without specific prior written permission.
+
+This software is provided by the copyright holders and contributors "as is"
+and any express or implied warranties, including, but not limited to, the
+implied warranties of merchantability and fitness for a particular purpose
+are disclaimed. In no event shall the copyright owner or contributors be
+liable for any direct, indirect, incidental, special, exemplary, or
+consequential damages (including, but not limited to, procurement of
+substitute goods or services; loss of use, data, or profits; or business
+interruption) however caused and on any theory of liability, whether in
+contract, strict liability, or tort (including negligence or otherwise)
+arising in any way out of the use of this software, even if advised of the
+possibility of such damage.
+
+=cut

Added: trafficserver/site/trunk/lib/doxy-link.pm
URL: 
http://svn.apache.org/viewvc/trafficserver/site/trunk/lib/doxy-link.pm?rev=1369371&view=auto
==============================================================================
--- trafficserver/site/trunk/lib/doxy-link.pm (added)
+++ trafficserver/site/trunk/lib/doxy-link.pm Sat Aug  4 16:54:00 2012
@@ -0,0 +1,82 @@
+package doxy;
+use strict;
+use warnings;
+
+require 'WikiVar.pm';
+require 'DoxygenSymbol.pm';
+
+use File::Spec;
+use Carp;
+use JSON;
+use LWP::Simple ();
+
+# URL for Doxygen content
+use constant DOXY_URL => 'http://people.apache.org/~amc/ats/doc/html';
+# Local file system path for symbol table
+use constant SYMBOL_TABLE_PATH => 'doxy-sym.json';
+# URL for externally base symbol table
+use constant SYMBOL_TABLE_URL => 
'http://people.apache.org/~amc/ats/doxy-sym.json';
+# Flag for data loaded
+our $LOADED_P = 0;
+# The symbol table
+our $SYMBOLS;
+# Array of source files
+our $FILES;
+# Our instance of WikiVar
+our $WV = Text::WikiVar->new(KEYWORD_MARK => ':', DELIMITER => '[');
+
+sub load_symbol_table {
+    my $path = SYMBOL_TABLE_PATH;
+    my $content;
+    
+    # Try to get an up to date copy.
+    my $http_code = LWP::Simple::mirror(SYMBOL_TABLE_URL, $path);
+    
+    if (open(my $f,"<",$path)) {
+        local $/ = undef;
+        my $content = <$f>;
+        my $data = from_json($content);
+        $SYMBOLS = $data->{symbols};
+        $FILES = $data->{files};
+        # Need the symbol elements to be of the right type
+        while (my ($key, $value) = each %$SYMBOLS) {
+            bless $value, 'DoxygenSymbol';
+        }
+        close $f;
+    } else {
+        warn "Failed to open Doxygen symbol table.\n";
+    }
+    $LOADED_P = 1;
+}
+
+$WV->assign('dox', sub {
+    load_symbol_table unless $LOADED_P;
+    
+    my $args = shift;
+    my $link; # output is constructed here
+    my $name = $args->[1];
+    my $data = $SYMBOLS->{$name};
+    
+    return "[Symbol $name not found]" unless $data;
+    
+    # Check to see if we want the source file instead.
+    my $file = $FILES->[$data->src_idx];
+    if ($args->{':src_file'}) {
+        my $src = unmangle_file_name($file);
+        $data = $SYMBOLS->{$src};
+        return "[Source file $src for symbol $name not found]" unless $data;
+    }
+    # Generate the link
+    my $url = DOXY_URL . '/' . $file;
+    $url .= '#' . $data->id if $data->id;
+    $link = "<a href=\"$url\"";
+    if ($data->arg_text) {
+        $link .= ' alt="'. $data->name . $data->arg_text . '"';
+    } elsif ($data->text) {
+        $link .= ' alt="' . $data->text . '"'
+    }
+    $link .= '>' . $data->name . '</a>';
+    return $link;
+});
+
+1;

Added: trafficserver/site/trunk/lib/doxy-sym-generate.pl
URL: 
http://svn.apache.org/viewvc/trafficserver/site/trunk/lib/doxy-sym-generate.pl?rev=1369371&view=auto
==============================================================================
--- trafficserver/site/trunk/lib/doxy-sym-generate.pl (added)
+++ trafficserver/site/trunk/lib/doxy-sym-generate.pl Sat Aug  4 16:54:00 2012
@@ -0,0 +1,91 @@
+# Scrape the Doxygen output to get a symbol table
+# This is a stand alone script to be run against the Doxygen output to
+# generate a symbol table. That table is loaded during a documentation
+# build. It should be stored in an HTTP acccessible location and the
+# build script tweaked to know that location so it can update its local
+# copy as needed.
+
+use strict;
+use JSON qw(-convert_blessed_universally);
+use File::Basename;
+
+require 'DoxygenSymbol.pm';
+
+our %SYMBOLS; # Symbol table, name -> DoxygenSymbol
+our @FILES; # Array of files.
+
+use constant DOXYGEN_BASE_DIR => '/sys/cygwin/home/amc/ats/doc/html';
+
+sub obtain_sym {
+    my ($table, $key, $src_idx) = @_;
+    my $zret = $table->{$key};
+    $zret = $table->{$key} = DoxygenSymbol->new(id => $key, src_idx => 
$src_idx) unless $zret;
+    return $zret;
+}
+
+sub load_doxy_file {
+    my $zret = 1;
+    my ($src_idx) = @_;
+    my $path = $FILES[$src_idx];
+    my %items; # temp hash keyed by ID
+    if (open(my $f,"<",$path)) {
+        # Gather the pieces of the method data together.
+        while (<$f>) {
+            my $sym = undef;
+            if (m/class="mdescRight">([^<]*)<.*href="#([^"]*)"/) {
+                my $text = $1;
+                $text =~ m/(\S+(?:\s+\S+)*)/;
+                $text = $1;
+                $sym = obtain_sym(\%items, $2, $src_idx);
+                $sym->text($text);
+            } elsif (m/doxytag: 
member="([^"]*)".*ref="([^"]*)".*args="([^"]*)"/) {
+                $sym = obtain_sym(\%items, $2, $src_idx);
+                $sym->name($1);
+                $sym->arg_text($3);
+            }
+        }
+        
+        # Put the source file in as a symbol
+        my $name = doxy::unmangle_file_name(basename($path));
+        if ($name =~  m/[.](?:cc|h|hpp)$/) {
+            $SYMBOLS{$name} = DoxygenSymbol->new(name => $name, src_idx => 
$src_idx, text => 'source file');
+        }
+
+        # Load the actual symbols in to the symbol table
+        while (my ($key, $value) = each %items) {
+            my $name = $value->name;
+            $name  =~ s/^[[:alnum:]]+[.](?:h|cc|hpp):://; # strip file name as 
leading namespace
+            $value->name($name);
+            
+            my $sym = $SYMBOLS{$name};
+            if (not $sym) {
+                $SYMBOLS{$name} = $value;
+            } elsif (ref $sym) {
+                push(@$sym, $value);
+            } else {
+                $SYMBOLS{$name} = [ $sym, $value ];
+            }
+        }
+        close $f;
+    } else {
+        $zret = 0;
+    }
+    return $zret;
+}
+
+sub load_sym_file {
+    
+}
+
+chdir(DOXYGEN_BASE_DIR);
+for (glob('*.html')) {
+    push(@FILES, $_);
+}
+for (my $idx = 0 ; $idx < scalar(@FILES) ; ++$idx) {
+    load_doxy_file($idx);
+}
+
+if (open(my $f,">",'/tmp/doxy-sym.json')) {
+    print $f to_json({files => \@FILES , symbols => \%SYMBOLS}, { 
allow_blessed => 1, convert_blessed => 1, indent => 1 });
+}
+exit 0;

Propchange: trafficserver/site/trunk/lib/doxy-sym-generate.pl
------------------------------------------------------------------------------
    svn:executable = *

Modified: trafficserver/site/trunk/lib/view.pm
URL: 
http://svn.apache.org/viewvc/trafficserver/site/trunk/lib/view.pm?rev=1369371&r1=1369370&r2=1369371&view=diff
==============================================================================
--- trafficserver/site/trunk/lib/view.pm (original)
+++ trafficserver/site/trunk/lib/view.pm Sat Aug  4 16:54:00 2012
@@ -2,6 +2,7 @@ package view;
 
 use strict;
 use warnings;
+require 'doxy-link.pm';
 use Dotiac::DTL qw/Template/;
 use Dotiac::DTL::Addon::markup;
 use ASF::Util qw/read_text_file shuffle/;
@@ -427,9 +428,14 @@ sub GetSource {
     my ($stem, $lang, $ext) = view::_SplitContentName($file);
     if ($node->langToFile($lang) ne $file) {
         my $idx = $node->indexOfStem($stem);
-        die "Failed to find source for '$path'.\n" if $idx < 0;
-        $src = $node->srcAt($idx);
-        die "Failed to find source for '$path'.\n" unless $file eq 
$src->langToFile($lang);
+        $src = undef;
+        if ($idx >= 0) {
+            # If we don't find it in the index, that's tolerable
+            # it means the file is there but it's not in order.txt
+            # so just ignore it by return undef
+            $src = $node->srcAt($idx);
+            die "Failed to find source for '$path'.\n" unless $file eq 
$src->langToFile($lang);
+        }
     }
     return wantarray ? ( $src , $lang ) : $src;
 }
@@ -468,6 +474,11 @@ sub single_narrative {
     $TARGET_BASE = $ENV{TARGET_BASE};
 
     my ($src, $lang) = view::Node::GetSource($file);
+    if (not $src) {
+        warn "Ignoring '$file' because an order.txt was present without this 
stem.\n";
+        return '';
+    }
+    
     $args{langs} = langs($src, $lang);
     $args{lang} = $lang;
     # copy over file local vars, including content.
@@ -478,7 +489,7 @@ sub single_narrative {
     # Compute page local nav links.
     nav_links($src, \%args) if $args{headers}{navigation};
     
-    return Dotiac::DTL::Template($template)->render(\%args), html => \%args;
+    return 
$doxy::WV->process(Dotiac::DTL::Template($template)->render(\%args)), html => 
\%args;
 }
 
 # Similar to single_narrative but does no auto navigation.


Reply via email to