summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorfielding <fielding@13f79535-47bb-0310-9956-ffa450edef68>2001-02-18 13:36:15 +0000
committerfielding <fielding@13f79535-47bb-0310-9956-ffa450edef68>2001-02-18 13:36:15 +0000
commit8e603d3b136118876d73b6f4658346a0e5279611 (patch)
tree874549ae9173dd1849582aa36147f063062f6c53
parent5762abb27eeaf7449fcf5f71d95860e8cb98e2a9 (diff)
downloadlibapr-8e603d3b136118876d73b6f4658346a0e5279611.tar.gz
Moved from apr/helpers to apr/build (without changes).
scandoc has been renamed to scandoc.pl default.pl has been renamed to scandoc_template.pl git-svn-id: http://svn.apache.org/repos/asf/apr/apr/trunk@61251 13f79535-47bb-0310-9956-ffa450edef68
-rwxr-xr-xbuild/MakeEtags39
-rwxr-xr-xbuild/PrintPath116
-rw-r--r--build/cvstodsp5.pl43
-rw-r--r--build/dsp5tocvs.pl46
-rw-r--r--build/make_export.awk54
-rwxr-xr-xbuild/mkdep.sh12
-rwxr-xr-xbuild/mkdir.sh35
-rw-r--r--build/rules.mk.in168
-rwxr-xr-xbuild/scandoc.pl1339
-rw-r--r--build/scandoc_template.pl518
10 files changed, 2370 insertions, 0 deletions
diff --git a/build/MakeEtags b/build/MakeEtags
new file mode 100755
index 000000000..1b030a3fc
--- /dev/null
+++ b/build/MakeEtags
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+# This file illustrates how to generate a useful TAGS file via etags
+# for emacs. This should be invoked from the src directory i.e.:
+# > helpers/MakeEtags
+# and will create a TAGS file in the src directory.
+
+# This script falls under the Apache License.
+# See http://www.apache.org/docs/LICENSE
+
+# Once you have created src/TAGS in emacs you'll need to setup
+# tag-table-alist with an entry to assure it finds the single src/TAGS
+# file from the many source directories. Something along these lines:
+# (setq tag-table-alist
+# '(("/home/me/work/apache-1.3/src/"
+# . "/home/me/work/apache-1.3/src/")
+# ))
+
+# This requires a special version of etags, i.e. the
+# one called "Exuberant ctags" available at:
+# http://fly.hiwaay.net/~darren/ctags/
+# Once that is setup you'll need to point to the
+# executable here:
+
+etags=~/local/bin/etags
+
+# Exuberant etags is necessary since it can ignore some defined symbols
+# that obscure the function signatures.
+
+ignore=AP_DECLARE,AP_DECLARE_NONSTD,__declspec
+
+# Create an etags file at the root of the source
+# tree, then create symbol links to it from each
+# directory in the source tree. By passing etags
+# absolute pathnames we get a tag file that is
+# NOT portable when we move the directory tree.
+
+find . -name '*.[ch]' -print | $etags -I "$ignore" -L -
+
diff --git a/build/PrintPath b/build/PrintPath
new file mode 100755
index 000000000..68435f374
--- /dev/null
+++ b/build/PrintPath
@@ -0,0 +1,116 @@
+#!/bin/sh
+# Look for program[s] somewhere in $PATH.
+#
+# Options:
+# -s
+# Do not print out full pathname. (silent)
+# -pPATHNAME
+# Look in PATHNAME instead of $PATH
+#
+# Usage:
+# PrintPath [-s] [-pPATHNAME] program [program ...]
+#
+# Initially written by Jim Jagielski for the Apache configuration mechanism
+# (with kudos to Kernighan/Pike)
+#
+# This script falls under the Apache License.
+# See http://www.apache.org/docs/LICENSE
+
+##
+# Some "constants"
+##
+pathname=$PATH
+echo="yes"
+
+##
+# Find out what OS we are running for later on
+##
+os=`(uname) 2>/dev/null`
+
+##
+# Parse command line
+##
+for args in $*
+do
+ case $args in
+ -s ) echo="no" ;;
+ -p* ) pathname="`echo $args | sed 's/^..//'`" ;;
+ * ) programs="$programs $args" ;;
+ esac
+done
+
+##
+# Now we make the adjustments required for OS/2 and everyone
+# else :)
+#
+# First of all, all OS/2 programs have the '.exe' extension.
+# Next, we adjust PATH (or what was given to us as PATH) to
+# be whitespace seperated directories.
+# Finally, we try to determine the best flag to use for
+# test/[] to look for an executable file. OS/2 just has '-r'
+# but with other OSs, we do some funny stuff to check to see
+# if test/[] knows about -x, which is the prefered flag.
+##
+
+if [ "x$os" = "xOS/2" ]
+then
+ ext=".exe"
+ pathname=`echo -E $pathname |
+ sed 's/^;/.;/
+ s/;;/;.;/g
+ s/;$/;./
+ s/;/ /g
+ s/\\\\/\\//g' `
+ test_exec_flag="-r"
+else
+ ext="" # No default extensions
+ pathname=`echo $pathname |
+ sed 's/^:/.:/
+ s/::/:.:/g
+ s/:$/:./
+ s/:/ /g' `
+ # Here is how we test to see if test/[] can handle -x
+ testfile="pp.t.$$"
+
+ cat > $testfile <<ENDTEST
+#!/bin/sh
+if [ -x / ] || [ -x /bin ] || [ -x /bin/ls ]; then
+ exit 0
+fi
+exit 1
+ENDTEST
+
+ if `/bin/sh $testfile 2>/dev/null`; then
+ test_exec_flag="-x"
+ else
+ test_exec_flag="-r"
+ fi
+ rm -f $testfile
+fi
+
+for program in $programs
+do
+ for path in $pathname
+ do
+ if [ $test_exec_flag $path/${program}${ext} ] && \
+ [ ! -d $path/${program}${ext} ]; then
+ if [ "x$echo" = "xyes" ]; then
+ echo $path/${program}${ext}
+ fi
+ exit 0
+ fi
+
+# Next try without extension (if one was used above)
+ if [ "x$ext" != "x" ]; then
+ if [ $test_exec_flag $path/${program} ] && \
+ [ ! -d $path/${program} ]; then
+ if [ "x$echo" = "xyes" ]; then
+ echo $path/${program}
+ fi
+ exit 0
+ fi
+ fi
+ done
+done
+exit 1
+
diff --git a/build/cvstodsp5.pl b/build/cvstodsp5.pl
new file mode 100644
index 000000000..d37442735
--- /dev/null
+++ b/build/cvstodsp5.pl
@@ -0,0 +1,43 @@
+use IO::File;
+use File::Find;
+
+chdir '..';
+find(\&tovc5, '.');
+
+sub tovc5 {
+
+ if (m|.dsp$|) {
+ $oname = $_;
+ $tname = '.#' . $_;
+ $verchg = 0;
+ $srcfl = new IO::File $oname, "r" || die;
+ $dstfl = new IO::File $tname, "w" || die;
+ while ($src = <$srcfl>) {
+ if ($src =~ s|Format Version 6\.00|Format Version 5\.00|) {
+ $verchg = -1;
+ }
+ if ($src =~ s|^(# ADD CPP .*)/ZI (.*)|$1/Zi $2|) {
+ $verchg = -1;
+ }
+ if ($src =~ s|^(# ADD BASE CPP .*)/ZI (.*)|$1/Zi $2|) {
+ $verchg = -1;
+ }
+ if ($src !~ m|^# PROP AllowPerConfigDependencies|) {
+ print $dstfl $src; }
+ else {
+ $verchg = -1;
+
+ }
+ }
+ undef $srcfl;
+ undef $dstfl;
+ if ($verchg) {
+ unlink $oname || die;
+ rename $tname, $oname || die;
+ print "Converted VC6 project " . $oname . " to VC5 in " . $File::Find::dir . "\n";
+ }
+ else {
+ unlink $tname;
+ }
+ }
+} \ No newline at end of file
diff --git a/build/dsp5tocvs.pl b/build/dsp5tocvs.pl
new file mode 100644
index 000000000..9686b4363
--- /dev/null
+++ b/build/dsp5tocvs.pl
@@ -0,0 +1,46 @@
+use IO::File;
+use File::Find;
+
+chdir '..';
+find(\&tovc6, '.');
+
+sub tovc6 {
+
+ if (m|.dsp$|) {
+ $oname = $_;
+ $tname = '.#' . $_;
+ $verchg = 0;
+ $srcfl = new IO::File $_, "r" || die;
+ $dstfl = new IO::File $tname, "w" || die;
+ while ($src = <$srcfl>) {
+ if ($src =~ s|Format Version 5\.00|Format Version 6\.00|) {
+ $verchg = -1;
+ }
+ if ($src =~ s|^(# ADD CPP .*)/Zi (.*)|$1/ZI $2|) {
+ $verchg = -1;
+ }
+ if ($src =~ s|^(# ADD BASE CPP .*)/Zi (.*)|$1/ZI $2|) {
+ $verchg = -1;
+ }
+ if ($src =~ s|^(!MESSAGE .*)\\\n|$1|) {
+ $cont = <$srcfl>;
+ $src = $src . $cont;
+ $verchg = -1;
+ }
+ print $dstfl $src;
+ if ($verchg && $src =~ m|^# Begin Project|) {
+ print $dstfl "# PROP AllowPerConfigDependencies 0\n";
+ }
+ }
+ undef $srcfl;
+ undef $dstfl;
+ if ($verchg) {
+ unlink $oname || die;
+ rename $tname, $oname || die;
+ print "Converted VC5 project " . $oname . " to VC6 in " . $File::Find::dir . "\n";
+ }
+ else {
+ unlink $tname;
+ }
+ }
+}
diff --git a/build/make_export.awk b/build/make_export.awk
new file mode 100644
index 000000000..7d61a55fa
--- /dev/null
+++ b/build/make_export.awk
@@ -0,0 +1,54 @@
+# Based on Ryan Bloom's make_export.pl
+
+/^#[ \t]*if(def)? (AP[RU]?_|!?defined).*/ {
+ if (old_filename != FILENAME) {
+ if (old_filename != "") printf("%s", line)
+ macro_no = 0
+ found = 0
+ count = 0
+ old_filename = FILENAME
+ line = ""
+ }
+ macro_stack[macro_no++] = macro
+ macro = substr($0, length($1)+2)
+ count++
+ line = line macro "\n"
+ next
+}
+
+/^#[ \t]*endif/ {
+ if (count > 0) {
+ count--
+ line = line "/" macro "\n"
+ macro = macro_stack[--macro_no]
+ }
+ if (count == 0) {
+ if (found != 0) {
+ printf("%s", line)
+ }
+ line = ""
+ }
+ next
+}
+
+/^[ \t]*(AP[RU]?_DECLARE[^(]*[(])?(const[ \t])?[a-z_]+[ \t\*]*[)]?[ \t]+[*]?([A-Za-z0-9_]+)\(/ {
+ if (count) {
+ found++
+ }
+ for (i = 0; i < count; i++) {
+ line = line "\t"
+ }
+ sub("^[ \t]*(AP[UR]?_DECLARE[^(]*[(])?(const[ \t])?[a-z_]+[ \t\*]*[)]?[ \t]+[*]?", "");
+ sub("[(].*", "");
+ line = line $0 "\n"
+
+ if (count == 0) {
+ printf("%s", line)
+ line = ""
+ }
+ next
+}
+
+END {
+ printf("%s", line)
+}
diff --git a/build/mkdep.sh b/build/mkdep.sh
new file mode 100755
index 000000000..510bdc02f
--- /dev/null
+++ b/build/mkdep.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+#
+# 1) remove everything after the DO NOT REMOVE
+# 2) generate the dependencies, adding them to the end of Makefile.new
+# 3) move the Makefile.new back into place
+#
+# Note that we use && to ensure that Makefile is not changed if an error
+# occurs during the process
+#
+sed -ne '1,/^# DO NOT REMOVE/p' Makefile > Makefile.new \
+ && gcc -MM $* | sed -e "s/\.o:/\.lo:/" >> Makefile.new \
+ && mv Makefile.new Makefile
diff --git a/build/mkdir.sh b/build/mkdir.sh
new file mode 100755
index 000000000..4cd33c567
--- /dev/null
+++ b/build/mkdir.sh
@@ -0,0 +1,35 @@
+#!/bin/sh
+##
+## mkdir.sh -- make directory hierarchy
+##
+## Based on `mkinstalldirs' from Noah Friedman <friedman@prep.ai.mit.edu>
+## as of 1994-03-25, which was placed in the Public Domain.
+## Cleaned up for Apache's Autoconf-style Interface (APACI)
+## by Ralf S. Engelschall <rse@apache.org>
+##
+#
+# This script falls under the Apache License.
+# See http://www.apache.org/docs/LICENSE
+
+
+umask 022
+errstatus=0
+for file in ${1+"$@"} ; do
+ set fnord `echo ":$file" |\
+ sed -e 's/^:\//%/' -e 's/^://' -e 's/\// /g' -e 's/^%/\//'`
+ shift
+ pathcomp=
+ for d in ${1+"$@"}; do
+ pathcomp="$pathcomp$d"
+ case "$pathcomp" in
+ -* ) pathcomp=./$pathcomp ;;
+ esac
+ if test ! -d "$pathcomp"; then
+ echo "mkdir $pathcomp" 1>&2
+ mkdir "$pathcomp" || errstatus=$?
+ fi
+ pathcomp="$pathcomp/"
+ done
+done
+exit $errstatus
+
diff --git a/build/rules.mk.in b/build/rules.mk.in
new file mode 100644
index 000000000..7bc932ffc
--- /dev/null
+++ b/build/rules.mk.in
@@ -0,0 +1,168 @@
+# ====================================================================
+# The Apache Software License, Version 1.1
+#
+# Copyright (c) 2000-2001 The Apache Software Foundation. All rights
+# reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. 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.
+#
+# 3. The end-user documentation included with the redistribution,
+# if any, must include the following acknowledgment:
+# "This product includes software developed by the
+# Apache Software Foundation (http://www.apache.org/)."
+# Alternately, this acknowledgment may appear in the software itself,
+# if and wherever such third-party acknowledgments normally appear.
+#
+# 4. The names "Apache" and "Apache Software Foundation" must
+# not be used to endorse or promote products derived from this
+# software without prior written permission. For written
+# permission, please contact apache@apache.org.
+#
+# 5. Products derived from this software may not be called "Apache",
+# nor may "Apache" appear in their name, without prior written
+# permission of the Apache Software Foundation.
+#
+# THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 APACHE SOFTWARE FOUNDATION OR
+# ITS 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.
+# ====================================================================
+#
+# This software consists of voluntary contributions made by many
+# individuals on behalf of the Apache Software Foundation. For more
+# information on the Apache Software Foundation, please see
+# <http://www.apache.org/>.
+#
+
+#
+# rules.mk: standard rules for APR
+#
+
+@SET_MAKE@
+
+#
+# Configuration variables
+#
+top_builddir=@top_builddir@
+
+CC=@CC@
+AWK=@AWK@
+LIBTOOL=@LIBTOOL@
+
+CFLAGS=@CFLAGS@ @OPTIM@
+CPPFLAGS=@CPPFLAGS@ $(INCLUDES)
+LIBS=@LIBS@
+LDFLAGS=@LDFLAGS@
+
+RM=@RM@
+SHELL=@SHELL@
+
+MKEXPORT=@APR_MKEXPORT@
+MKDEP=@APR_MKDEP@
+SCANDOC=@APR_SCANDOC@
+
+### make LTFLAGS somewhat variable?
+LTFLAGS = --silent
+
+#
+# Basic macro setup
+#
+COMPILE = $(CC) $(CFLAGS) $(CPPFLAGS)
+LT_COMPILE = $(LIBTOOL) --mode=compile $(LTFLAGS) $(COMPILE) -c $< && touch $@
+
+LINK = $(LIBTOOL) --mode=link $(LTFLAGS) $(COMPILE) $(LDFLAGS) -o $@
+
+#
+# Standard build rules
+#
+all: all-recursive
+depend: depend-recursive
+clean: clean-recursive
+distclean: distclean-recursive
+extraclean: extraclean-recursive
+
+install: all-recursive
+
+
+all-recursive depend-recursive clean-recursive distclean-recursive \
+ extraclean-recursive:
+ @otarget=`echo $@ | sed s/-recursive//`; \
+ list='$(SUBDIRS)'; \
+ for i in $$list; do \
+ if test -d "$$i"; then \
+ target="$$otarget"; \
+ echo "Making $$target in $$i"; \
+ if test "$$i" = "."; then \
+ made_local=yes; \
+ target="local-$$target"; \
+ fi; \
+ (cd $$i && $(MAKE) $$target) || exit 1; \
+ fi; \
+ done; \
+ if test "$$otarget" = "all" && test -z "$(TARGETS)"; then \
+ made_local=n/a; \
+ fi; \
+ if test -z "$$made_local"; then \
+ $(MAKE) "local-$$otarget" || exit 1; \
+ fi
+
+local-clean: x-local-clean
+ $(RM) -f *.o *.lo *.a *.la *.so $(CLEAN_TARGETS) $(PROGRAMS)
+ $(RM) -rf .libs
+
+local-distclean: local-clean x-local-distclean
+ $(RM) -f Makefile $(DISTCLEAN_TARGETS)
+
+local-extraclean: local-distclean
+ @if test -n "$(EXTRACLEAN_TARGETS)"; then \
+ echo $(RM) -f $(EXTRACLEAN_TARGETS) ; \
+ $(RM) -f $(EXTRACLEAN_TARGETS) ; \
+ fi
+
+local-all: $(TARGETS)
+
+local-depend:
+ @if test -n "`ls *.c 2> /dev/null`"; then \
+ echo $(MKDEP) $(CFLAGS) $(CPPFLAGS) *.c ; \
+ $(MKDEP) $(CFLAGS) $(CPPFLAGS) *.c ; \
+ fi
+
+# to be filled in by the actual Makefile
+x-local-clean x-local-distclean:
+
+
+#
+# Implicit rules for creating outputs from input files
+#
+.SUFFIXES:
+.SUFFIXES: .c .lo .o
+
+.c.o:
+ $(COMPILE) -c $<
+
+.c.lo:
+ $(LT_COMPILE)
+
+.PHONY: all depend clean distclean extraclean install \
+ all-recursive depend-recursive clean-recursive distclean-recursive \
+ extraclean-recursive
+ local-all local-depend local-clean local-distclean local-extraclean \
+ x-local-clean x-local-distclean
diff --git a/build/scandoc.pl b/build/scandoc.pl
new file mode 100755
index 000000000..13043a306
--- /dev/null
+++ b/build/scandoc.pl
@@ -0,0 +1,1339 @@
+#!/usr/bin/perl
+#
+# ScanDoc - Version 0.12, A C/C++ Embedded Documentation Analyser
+# ----------------------------------------------------------------
+#
+# Distributed under the "Artistic License". See the file
+# "COPYING" that accompanies the ScanDoc distribution.
+#
+# See http://scandoc.sourceforge.net/ for more information and
+# complete documentation.
+#
+# (c) 1997 - 2000 Talin and others.
+
+require "ctime.pl";
+require "getopts.pl";
+
+# 1 = on (verbose); 0 = off
+$debug = 0;
+
+# Get the current date
+$date = &ctime(time);
+
+# Set the default tab size
+$tabSize = 4;
+
+$minorVersion = 12;
+$majorVersion = 0;
+$scandocURL = "http://scandoc.sourceforge.net/";
+
+# Set up default templates
+&Getopts( 'i:d:p:t:' );
+
+if ($#ARGV < 0) {
+ die "Usage: -i <doc-template> -p <output-path> -t<tabsize> -d<sym>=<value> [ <input-files> ... ]\n";
+}
+
+# Read the template
+if (!defined $opt_i) {
+ $opt_i = "default.pl";
+}
+&readTemplate( $opt_i );
+
+# Set the destination path.
+$destPath = "";
+$destPath = $opt_p if (defined($opt_p));
+
+# Set the tab size.
+$tabSize = $opt_t if (defined($opt_t));
+
+# Handle defines
+if ($opt_d) {
+ foreach $def (split( /,/, $opt_d )) {
+ if ($def =~ /\s*(\w*)\=(.*)/) {
+ $${1} = $2;
+ }
+ else {
+ $${1} = 1;
+ }
+ }
+}
+
+# For each input filename, parse it
+while ($srcfile = shift(@ARGV)) {
+
+ $linenumber = 0;
+ open( FILE, $srcfile ) || die "Can't open file $srcfile\n";
+ print STDERR "Reading \"$srcfile\"\n";
+
+ $docTag = 'description';
+ $docEmpty = 1;
+ $packageName = '.general';
+ $author = '';
+ $version = '';
+ $class = 0;
+ $_ = '';
+
+ while (&parseDeclaration( '' )) {}
+}
+
+# Collate subclasses and associate with class record.
+foreach $className (keys %subclasses) {
+ my $class = &classRecord( $className );
+
+ if ($class) {
+ my @subs = ();
+ # print STDERR "$className ", join( ',', @{$subclasses{ $className }} ), "\n";
+ foreach $subName ($subclasses{ $className }) {
+ if (&classRecord( $subName )) {
+ push @subs, $subName;
+ }
+ $class->{ 'subs' } = @subs;
+ }
+ }
+}
+
+# Turn packages into objects. Special case for "default" package.
+foreach $pkg (keys %packages)
+{
+ # print STDERR $pkg, "\n";
+ bless $packages{ $pkg }, PackageRecord;
+ if ($pkg eq '.general') {
+ $packages{ $pkg }{ 'name' } = "General";
+ }
+ else {
+ $packages{ $pkg }{ 'name' } = $pkg;
+ }
+ # print STDERR $packages{ $pkg }->Classes(), "\n";
+}
+
+# Execute template file
+# print STDERR $docTemplate; # For debugging
+eval $docTemplate;
+print STDERR $@;
+
+exit;
+
+# ======================= Subroutines ================================
+
+# Read a line of input, and remove blank lines and preprocessor directives.
+sub rdln {
+ my ($skip_next_line) = 0;
+ if (defined ($_)) {
+ my ($previous_line) = $_;
+ while ( (/^(\s*|\#.*)$/ || $skip_next_line ) && ($_ = <FILE>)) {
+ if ($previous_line =~ m/\\\s*/) { $skip_next_line = 1; }
+ else { $skip_next_line = 0; }
+ $previous_line = $_;
+ $linenumber++;
+ if ($debug) { print STDERR "(0:$srcfile) $linenumber.\n"; }
+ }
+ }
+ # Dispose of Apache specific macros
+ removeApacheMacros();
+}
+
+# Don't skip "#"
+sub rdln2 {
+ if (defined ($_)) {
+ while (/^(\s*)$/ && ($_ = <FILE>)) {$linenumber++; if ($debug) { print STDERR "(0:$srcfile) $linenumber.\n"; } }
+ }
+}
+
+# Remove comments from current line
+sub removeComment {
+ s|//.*||;
+}
+
+# parsing functions
+sub matchKW { &rdln; return (s/^\s*($_[0])//, $1) if defined ($_); return (0, 0); }
+#sub matchStruct { &rdln; return (s/^\s*(struct|class)//, $1) if defined ($_); return (0, 0); }
+#sub matchPermission { &rdln; return (s/^\s*(public|private|protected)// && $1) if defined ($_); return (0,0); }
+sub matchID { &rdln; return (s/^\s*([A-Za-z_]\w*)//, $1) if defined ($_); return (0,0); }
+sub matchColon { &rdln; return (s/^\s*\://) if defined ($_); return 0; }
+sub matchComma { &rdln; return (s/^\s*\,//) if defined ($_); return 0; }
+sub matchSemi { &rdln; return (s/^\s*\;//) if defined ($_); return 0; }
+sub matchRBracket { &rdln; return (s/^\s*\{//) if defined ($_); return 0; }
+sub matchLBracket { &rdln; return (s/^\s*\}//) if defined ($_); return 0; }
+sub matchRParen { &rdln; return (s/^\s*\(//) if defined ($_); return 0; }
+sub matchLParen { &rdln; return (s/^\s*\)//) if defined ($_); return 0; }
+sub matchRAngle { &rdln; return (s/^\s*\<//) if defined ($_); return 0; }
+sub matchLAngle { &rdln; return (s/^\s*\>//) if defined ($_); return 0; }
+sub matchDecl { &rdln; return (s/^(\s*[\s\w\*\[\]\~\&\n\:]+)//, $1) if defined ($_); return (0, 0); }
+sub matchOper { &rdln; return (s/^\s*([\~\&\^\>\<\=\!\%\*\+\-\/\|\w]*)// && $1) if defined ($_); return 0; }
+sub matchFuncOper { &rdln; return (s/^\s*(\(\))// && $1) if defined ($_); return 0; }
+sub matchAny { &rdln; return (s/^\s*(\S+)//, $1) if defined ($_); return (0, 0); }
+sub matchChar { &rdln; return (s/^(.)//, $1) if defined ($_); return (0, 0); }
+sub matchChar2 { &rdln2; return (s/^(.)//, $1) if defined ($_); return (0, 0); }
+sub matchString { &rdln; return (s/^\"(([^\\\"]|(\\.)))*\"//, $1) if defined ($_); return (0, 0); }
+
+# Skip to next semicolon
+sub skipToSemi {
+
+ while (!&matchSemi) {
+
+ &rdln;
+ s|//.*||; # Eat comments
+ if (&matchLBracket) {
+ &skipBody;
+ next;
+ }
+ last if !s/^\s*([^\s\{\;]+)//;
+ # print STDERR "$1 ";
+ }
+}
+
+# Skip function body
+sub skipBody {
+ local( $nest );
+
+ $nest = 1;
+
+ for (;;) {
+ if (&matchRBracket) { $nest++; }
+ elsif (&matchLBracket) {
+ $nest--;
+ last if !$nest;
+ }
+ else {
+ last if ((($valid,) = &matchKW( "[^\{\}]")) && !$valid);
+ }
+ }
+}
+
+# Skip a string. (multiline)
+sub skipString {
+ local( $char, $lastchar);
+ $lastchar = "\"";
+
+ for (;;) {
+ ($valid, $char) = &matchChar2;
+ if (($char eq "\"") && ($lastchar ne "\\")) { last; }
+ if ($lastchar eq "\\") { $lastchar = " "; }
+ else { $lastchar = $char; }
+ }
+}
+
+
+# Skip everything in parenthesis.
+sub skipParenBody {
+ local( $nest );
+
+ $nest = 1;
+
+ for (;;) {
+ if (&matchRParen) { $nest++; }
+ elsif (&matchLParen) {
+ $nest--;
+ last if !$nest;
+ }
+ else {
+ last if ((($valid,) = &matchKW( "[^\(\)]")) && !$valid);
+ }
+ }
+}
+
+# Parse (*name) syntax
+sub parseParenPointer {
+ if (s/^(\s*\(\s*\*)//) {
+ $decl .= $1;
+ $nest = 1;
+
+ for (;;) {
+ # Preserve spaces, eliminate in-line comments
+ &removeComment;
+ while (s/^(\s+)//) { $decl .= $1; &rdln; }
+
+ if (&matchRParen) { $nest++; $decl .= "("; }
+ elsif (&matchLParen) {
+ $decl .= ")";
+ $nest--;
+ last if !$nest;
+ }
+ elsif ((($valid, $d) = &matchKW( "[^\(\)]*")) && $valid) { $decl .= $d; }
+ else { last; }
+ }
+
+ # Just in case there are array braces afterwards.
+ while ((($valid, $d) = &matchDecl) && $valid) { $decl .= $d; }
+ }
+}
+
+# Parse template arguments
+sub matchAngleArgs {
+
+ if (&matchRAngle) {
+ local ($args, $nest);
+
+ $args = "&lt;";
+ $nest = 1;
+
+ for (;;) {
+ if (&matchRAngle) { $nest++; $args .= "&lt;"; }
+ elsif (&matchLAngle) {
+ $nest--;
+ $args .= "&gt;";
+ last if !$nest;
+ }
+ elsif ((($valid, $d) = &matchChar) && $valid) { $args .= $d; }
+ else { last; }
+ }
+ return $args;
+ }
+ else { return ''; }
+}
+
+# convert tabs to spaces
+sub expandTabs {
+ local ($text) = @_;
+ local ($n);
+
+ while (($n = index($text,"\t")) >= 0) {
+ substr($text, $n, 1) = " " x ($tabSize-($n % $tabSize));
+ }
+
+ return $text;
+}
+
+# Process a line of text from a "special" comment
+sub handleCommentLine {
+ local ($_) = @_;
+
+ if ($docEmpty) {
+ # Eliminate blank lines at the head of the doc.
+ return if (/^\s*$/);
+ }
+
+ # First, expand tabs.
+ $_ = &expandTabs( $_ );
+
+ # Remove gratuitous \s*\s (james)
+ s/(^|\n)\s*\*\s/$1/g;
+
+ # If it's one of the standard tags
+ if (s/^\s*\@(see|package|version|author|param|return|result|exception|keywords|deffunc|defvar|heading|todo)\s*//) {
+ my $tag = $1;
+ $tag = 'return' if ($tag eq 'result');
+
+ # for param and exception, split the param name and the text
+ # seperate them with tabs.
+ if ($tag eq "param" || $tag eq "exception") {
+ s/^\s*(\w+)\s*(.*)/\t$1\t$2/;
+ }
+ elsif ($tag eq "heading") {
+ # 'heading' is processed by the template, if at all.
+ $_ = "\@heading\t$_";
+ $tag = "description";
+ }
+ elsif ($tag eq 'todo') {
+ if ($todolist{ $srcfile } ne '') {
+ $todolist{ $srcfile } .= "\n";
+ }
+ }
+
+ # If it's @deffunc or @defvar
+ if ($tag =~ /def(.*)/) {
+
+ $type = $1;
+
+ # @deffunc and @defvar force a comment to be written out as if there was a
+ # declaration.
+ # Designed for use with macros and other constructs I can't parse.
+
+ if (/(\S+)\s+(.*)$/) {
+ $name = $1;
+ $decl = $2;
+ $dbname = &uniqueName( "$baseScope$name" );
+
+ my $entry = { 'type' => $type,
+ 'name' => $name,
+ 'longname'=> $name,
+ 'fullname'=> "$name $decl",
+ 'scopename'=>"$baseScope$name",
+ 'uname' => $dbname,
+ 'decl' => $decl,
+ 'package' => $packageName };
+
+ bless $entry, MemberRecord;
+
+ if ($class) {
+ $entry->{ 'class' } = "$context";
+ $class->{ 'members' }{ $dbname } = $entry;
+ }
+ else {
+ $packages{ $packageName }{ 'globals' }{ $dbname } = $entry;
+ }
+ $docTag = 'description';
+ &dumpComments( $entry );
+ return;
+ }
+ }
+ elsif ($tag eq 'package') {
+ s/^\s*//;
+ s/\s*$//;
+ $packageName = $_;
+ $docTag = 'description';
+ return;
+ }
+ elsif ($tag eq 'author') {
+ $author = $_;
+ $docTag = 'description';
+ return;
+ }
+ elsif ($tag eq 'version') {
+ $version = $_;
+ $docTag = 'description';
+ return;
+ }
+
+ $docTag = $tag;
+ }
+ elsif (/^\s*@\w+/) {
+ # any other line that begins with an @ should be inserted into the main
+ # description for later expansion.
+ $docTag = 'description';
+ }
+
+ # "To-do" lists are handled specially, and not associated with a class.
+ if ($docTag eq 'todo') {
+ $todolist{ $srcfile } .= $_;
+ return;
+ }
+
+ # Append to current doc tag, regardless of whether it's a new line
+ # or a continuation. Also mark this doc as non-empty.
+ $docTags{ $docTag } .= $_;
+ $docEmpty = 0;
+
+ # @see doesn't persist.
+ if ($docTag eq 'see') { $docTag = 'description'; }
+
+ # print STDERR ":$_";
+}
+
+# Clear doc tag information at end of class or file
+sub clearComments {
+
+ $docTag = 'description';
+ $docEmpty = 1;
+ %docTags = ();
+}
+
+# Add doc tag information to current documented item
+sub dumpComments {
+ local ($hashref) = @_;
+
+ if ($docEmpty == 0) {
+
+ if ($author ne '') { $hashref->{ 'author' } = $author; }
+ if ($version ne '') { $hashref->{ 'version' } = $version; }
+ $hashref->{ 'sourcefile' } = $srcfile;
+
+ # Store the tags for this documentation into the global doc symbol table
+ foreach $key (keys %docTags) {
+ my $data = $docTags{ $key };
+
+ $data =~ s/\s*$//;
+
+ $hashref->{ $key } = $data;
+ }
+ }
+
+ &clearComments();
+}
+
+# Generate a unique name from the given name.
+sub uniqueName {
+ local ($name) = @_;
+
+ # Duplicate doc entries need to be distinguished, so give them a different label.
+ while ($docs{ $name }) {
+ if ($name =~ /-(\d+)$/) {
+ $name = $` . "-" . ($1 + 1);
+ }
+ else { $name .= "-2"; }
+ }
+
+ $docs{ $name } = 1;
+ return $name;
+}
+
+# Get the current class record.
+sub classRecord {
+ local ($className) = @_;
+ local ($pkg) = $classToPackage{ $className };
+
+ if ($pkg) {
+ return $packages{ $pkg }{ 'classes' }{ $className };
+ }
+ return 0;
+}
+
+# Parse a declaration in the file
+sub parseDeclaration {
+
+ local ($context) = @_;
+ local ($baseScope) = '';
+ local ($decl);
+ my ($token);
+
+ if ($context) { $baseScope = $context . "::"; }
+
+ &rdln;
+
+ if (!defined ($_)) { return 0; }
+
+ if (s|^\s*//\*\s+||) {
+ # Special C++ comment
+ &handleCommentLine( $' );
+ $_ = ''; &rdln;
+ }
+ elsif (s|^\s*//||) {
+ # Ordinary C++ comment
+ $_ = '';
+ &rdln;
+ }
+ elsif (s|^\s*\/\*\*\s+||) {
+ # Special C comments
+
+ s/\={3,}|\-{3,}|\*{3,}//; # Eliminate banner strips
+ $text = '';
+ $docTag = 'description';
+
+ # Special comment
+ while (!/\*\//) { &handleCommentLine( $_ ); $text .= $_; $_ = <FILE>; $linenumber++; if ($debug) { print STDERR "(1) $linenumber\n."; }}
+ s/\={3,}|\-{3,}|\*{3,}//; # Eliminate banner strips
+ /\*\//;
+ &handleCommentLine( $` );
+ $text.= $`; $_ = $';
+ }
+ elsif (s|^\s*\/\*||) {
+ # Ordinary C comment
+ $text = "";
+
+ while (!/\*\//) { $text .= $_; $_ = <FILE>; $linenumber++; if ($debug) { print STDERR "(2) $linenumber\n."; }}
+ /\*\//;
+ $text.= $`; $_ = $';
+ }
+ elsif ((($valid, $tag) = &matchKW( "template")) && $valid) {
+ # Template definition
+ $args = &matchAngleArgs;
+ &rdln;
+
+ ##$tmplParams = $args; JAMES
+ $result = &parseDeclaration( $context );
+ ##$tmplParams = ''; JAMES
+ return $result;
+ }
+ elsif ((($valid, $tag) = &matchKW("class|struct")) && $valid) {
+ # Class or structure definition
+ local ($className,$class);
+
+ if ((($valid, $className) = &matchID) && $valid) {
+
+ return 1 if (&matchSemi); # Only a struct tag
+
+ # A class instance
+ if ((($valid,)=&matchID) && $valid) {
+ &matchSemi;
+ return 1;
+ }
+
+ my $fullName = "$baseScope$className"; ##$tmplParams"; JAMES
+ # print STDERR "CLASS $fullName\n";
+
+ my @bases = ();
+
+ if (&matchColon) {
+
+ for (;;) {
+ my $p;
+ &matchKW( "virtual" );
+ $perm = "private";
+ if ((($valid, $p) = &matchKW( "public|private|protected" )) && $valid) { $perm = $p; }
+ &matchKW( "virtual" );
+
+ last if !( (($valid, $base) = &matchID) && $valid );
+
+ push @bases, $base;
+ push @{ $subclasses{ $base } }, $fullName;
+ # print STDERR " : $perm $base\n";
+ last if !&matchComma;
+ }
+ }
+
+ # print STDERR "\n";
+ # print STDERR "parsing class $fullName\n";
+
+ if ($docEmpty == 0) {
+ $class = { 'type' => $tag,
+ 'name' => $fullName,
+ 'longname'=> "$tag $className",
+ 'fullname'=> "$tag $className",
+ 'scopename'=> "$tag $fullName",
+ 'uname' => $fullName,
+ 'bases' => \@bases,
+ 'package' => $packageName,
+ 'members' => {} };
+
+ # print STDERR "$className: @bases\n";
+
+ bless $class, ClassRecord;
+
+ print STDERR " parsing class $fullName\n";
+ # $classToPackage{ $className } = $packageName;
+ $classToPackage{ $fullName } = $packageName;
+ # $classList{ $className } = $class;
+ $classList{ $fullName } = $class;
+ $packages{ $packageName }{ 'classes' }{ $fullName } = $class;
+ &dumpComments( $class );
+ }
+
+ if (&matchRBracket) {
+ local ($perm) = ("private");
+
+ while (!&matchLBracket) {
+ my $p;
+ if ((($valid, $p) = &matchKW( "public\:|private\:|protected\:" )) && $valid) {
+ $perm = $p;
+ }
+ else {
+ &parseDeclaration( $fullName )
+ || die "Unmatched brace! line = $linenumber\n";
+ }
+ }
+
+ &matchSemi;
+ }
+
+ &clearComments;
+ }
+ }
+ elsif ( ((($valid,)=&matchKW( "enum")) && $valid) || ((($valid,)=&matchKW( "typedef" )) && $valid)) {
+ &skipToSemi;
+ }
+ elsif ((($valid,)=&matchKW( "friend\s*class" )) && $valid) {
+ &skipToSemi;
+ }
+ elsif ((($valid, $token) = &matchKW("extern\\s*\\\"C\\\"")) && $valid) {
+ &matchRBracket;
+ while (!&matchLBracket) {
+ &parseDeclaration( '' ) || die "Unmatched brace! line = $linenumber\n";
+ }
+ &matchSemi;
+ }
+ # elsif ($kw = &matchID) {
+ # $type = "$kw ";
+ #
+ # if ($kw =~/virtual|static|const|volatile/) {
+ # $type .= &typ;
+ # }
+ # }
+ elsif ((($valid, $decl) = &matchDecl) && $valid) {
+ my ($instanceClass) = "";
+
+ # print STDERR "DECLARATION=$decl, REST=$_, baseScope=$baseScope\n";
+
+ return 1 if ($decl =~ /^\s*$/);
+
+ if (!($class)) {
+ if ($decl =~ s/(\S*\s*)(\S+)\:\:(\S+)\s*$/$1$3/) {
+ $instanceClass = $2;
+ }
+ }
+
+ # Eliminate in-line comments
+ &removeComment;
+
+ # Check for multi-line declaration
+ while ((($valid, $d) = &matchDecl) && $valid) { $decl .= $d; }
+
+ # Handle template args, but don't let operator overloading confuse us!
+ $tempArgs = '';
+ if (!($decl =~ /\boperator\b/) && ($tempArgs = &matchAngleArgs)) {
+ $tempArgs = $decl . $tempArgs;
+ $decl = '';
+ while ((($valid, $d) = &matchDecl) && $valid) { $decl .= $d; }
+ }
+
+ # Look for (*name) syntax
+ &parseParenPointer;
+
+ # Special handling for operator... syntax
+ $oper = "";
+ if ($decl =~ s/\boperator\b(.*)/operator/) {
+ $oper = $1;
+ $oper .= &matchOper;
+ # If, after all that there's no opers, then try a () operator
+ if (!($oper =~ /\S/)) { $oper .= &matchFuncOper; }
+ }
+
+ ($type,$mod,$decl) = $decl =~ /([\s\w]*)([\s\*\&]+\s?)(\~?\w+(\[.*\])*)/;
+
+ $type = $tempArgs . $type;
+ $decl .= $oper;
+
+ if ($mod =~ /\s/) { $type .= $mod; $mod = ""; }
+
+ for (;;) {
+
+ # print STDERR "Looping: $type/$mod/$decl\n";
+
+ if (&matchRParen) {
+ $nest = 1;
+ $args = "";
+
+ for (;;) {
+ # print STDERR "Argloop $_\n";
+
+ # Process argument lists.
+
+ # Preserve spaces, eliminate in-line comments
+ # REM: Change this to save inline comments and automatically
+ # generate @param clauses
+ s|//.*||;
+ while (s/^(\s+)//) { $args .= " "; &rdln; }
+
+ if (&matchRParen) { $nest++; $args .= "("; }
+ elsif (&matchLParen) {
+ $nest--;
+ last if !$nest;
+ $args .= ")";
+ }
+ elsif ((($valid, $d) = &matchKW( "[\,\=\.\:\-]" )) && $valid) { $args .= $d; }
+ elsif ((($valid, $d) = &matchDecl) && $valid) { $args .= $d; }
+ elsif ((($valid, $d) = &matchAngleArgs) && $valid) { $args .= $d; }
+ elsif ((($valid, $d) = &matchString) && $valid) { $args .= "\"$d\""; }
+ else { last; }
+ }
+
+ # print STDERR "$type$mod$baseScope$decl($args);\n";
+
+ &matchKW( "const" );
+
+ # Search for any text within the name field
+ # if ($docTag && $decl =~ /\W*(~?\w*).*/)
+ if ($docEmpty == 0) {
+ $type =~ s/^\s+//;
+ $mod =~ s/\&/\&amp;/g;
+ $args =~ s/\&/\&amp;/g;
+ $args =~ s/\s+/ /g;
+ $dbname = &uniqueName( "$baseScope$decl" );
+
+ my $entry = { 'type' => 'func',
+ 'name' => $decl,
+ 'longname'=> "$decl()",
+ 'fullname'=> "$type$mod$decl($args)",
+ 'scopename'=>"$type$mod$baseScope$decl($args)",
+ 'uname' => $dbname,
+ 'decl' => "$type$mod$decl($args)",
+ 'package' => $packageName };
+
+ bless $entry, MemberRecord;
+
+ if ($class) {
+ $entry->{ 'class' } = "$context";
+ $class->{ 'members' }{ $dbname } = $entry;
+ }
+ elsif ($instanceClass) {
+ $class = &classRecord ($instanceClass);
+ if (!($class)) {
+ print STDERR "WARNING: Skipping \"$instanceClass\:\:$decl\". Class \"$instanceClass\" not declared ($linenumber).\n";
+ } else {
+ $entry->{ 'class' } = "$instanceClass";
+ $class->{ 'members' }{ $dbname } = $entry;
+ $class = 0;
+ }
+ }
+ else {
+ $packages{ $packageName }{ 'globals' }{ $dbname } = $entry;
+ }
+ &dumpComments( $entry );
+ }
+ else { &clearComments; }
+
+ s|//.*||;
+
+ # Constructor super-call syntax
+ if (&matchColon) {
+
+ # Skip over it.
+ for (;;) {
+ &rdln;
+ last if /^\s*(\{|\;)/;
+ last if !((($valid,)=&matchAny) && $valid);
+ }
+ }
+
+ last if &matchSemi;
+ if (&matchRBracket) { &skipBody; last; }
+ last if !&matchComma;
+ last if !((($valid, $decl) = &matchDecl) && $valid);
+
+ # Look for (*name) syntax
+ &parseParenPointer;
+
+ $decl =~ s/^\s*//;
+ $oper = "";
+ if ($decl =~ /\boperator\b/) {
+ $decl =~ s/\boperator\b(.*)/operator/;
+ $oper = $1 . &matchOper;
+ }
+ ($mod,$d) = $decl =~ /^\s*([\*\&]*)\s*(\~?\w+(\[.*\])*)/;
+ $decl .= $oper;
+ $decl = $d if $d ne "";
+ }
+ else {
+ s|//.*||;
+
+ $final = 0;
+
+ if ((($valid,)=&matchKW( "\=" )) && $valid) {
+ for (;;) {
+
+ if (&matchRBracket) {
+ &skipBody;
+ $final = 1;
+ last;
+ }
+
+ if (&matchSemi) {
+ $final = 1;
+ last;
+ }
+
+ # var = new ... (...)
+ if ((($valid,)=&matchKW("new")) && $valid) {
+ &matchKW("[A-Za-z_0-9 ]*");
+ if (&matchRParen) {
+ &skipParenBody;
+ }
+ }
+
+ # var = (.....) ...
+ if (&matchRParen) {
+ &skipParenBody;
+ }
+
+ # var = ... * ...
+ &matchKW ("[\/\*\-\+]*");
+
+ # var = "..."
+ if ((($valid,) = &matchKW ("[\"]")) && $valid) {
+ &skipString;
+ }
+ #&matchString;
+
+ last if /^\s*,/;
+ #last if !((($valid,)=&matchAny) && $valid);
+ last if !((($valid,)=&matchKW("[A-Za-z_0-9 \-]*")) && $valid);
+ if (&matchSemi) {
+ $final = 1;
+ last;
+ }
+ }
+ }
+
+ s|//.*||;
+
+ # void ~*&foo[];
+ # void foo[];
+ # void far*foo[];
+ # print STDERR "Decl: $type$mod$baseScope$decl;\n";
+
+ # Search for any text within the name field
+ if ($docEmpty == 0 && ($decl =~ /\W*(~?\w*).*/))
+ {
+ $mod =~ s/\&/\&amp;/g;
+ $name = $decl;
+
+ $dbname = &uniqueName( "$baseScope$1" );
+
+ my $entry = { 'type' => 'var',
+ 'name' => $1,
+ 'longname' => "$name",
+ 'fullname' => "$type$mod$decl",
+ 'scopename'=> "$baseScope$type$mod$decl",
+ 'uname' => $dbname,
+ 'decl' => "$type$mod$decl",
+ 'package' => $packageName };
+
+ bless $entry, MemberRecord;
+
+ if ($class) {
+ $entry->{ 'class' } = "$context";
+ $class->{ 'members' }{ $dbname } = $entry;
+ }
+ else {
+ $packages{ $packageName }{ 'globals' }{ $dbname } = $entry;
+ }
+ &dumpComments( $entry );
+ }
+ else { &clearComments; }
+
+ last if $final;
+ last if &matchSemi;
+ last if !&matchComma;
+ last if !((($valid, $decl) = &matchDecl) && $valid);
+
+ # Look for (*name) syntax
+ &parseParenPointer;
+
+ $decl =~ s/^\s*//;
+ ($mod,$d) = $decl =~ /^\s*([\*\&]*)(\~?\w+(\[.*\])*)/;
+ $decl = $d if $d ne "";
+ }
+ }
+ }
+ elsif ($context ne "" && /^\s*\}/) {
+ # print STDERR "Popping!\n";
+ return 1;
+ }
+ elsif (&matchRBracket) {
+ &skipBody;
+ }
+ elsif ((($valid, $token) = &matchAny) && $valid) {
+ # Comment in for debugging
+ # print STDERR "token: $token \n";
+ }
+ else { return 0; }
+
+ return 1;
+}
+
+# read a file into a string ( filename, default-value )
+sub readFile {
+ local ( $filename, $result ) = @_;
+
+ if ($filename && open( FILE, $filename )) {
+ $result = "";
+ while (<FILE>) { $result .= $_; }
+ close( FILE );
+ }
+ return $result;
+}
+
+# Read the entire document template and translate into PERL code.
+sub readTemplate {
+ local ( $filename ) = @_;
+ $docTemplate = '';
+ $indent = '';
+ $literal = 1; # We're in literal mode.
+
+ if (!-e $filename) {
+ if (-e "./templates/$filename") { $filename = "./templates/$filename"; }
+ elsif (-e "../templates/$filename") { $filename = "../templates/$filename"; }
+ else { die "Could not find template '$filename'.\n"; }
+ }
+
+ open( FILE, $filename ) || die "Error opening '$filename'.\n";
+ while (<FILE>) {
+ last if (/END/);
+
+ # if we found a code entry.
+ for (;;) {
+ &expandTabs( $_ );
+ if ($literal) {
+ # Check for beginning of code block.
+ if (s/^(.*)\<\<//) {
+ $line = $1;
+ if (substr( $line, 0, length( $indent ) ) eq $indent) {
+ substr( $line, 0, length( $indent ) ) = '';
+ }
+
+ if ($line ne '') {
+ $line =~ s/\"/\\\"/g;
+ $line =~ s/\$\((\w+)\.(\w+)\)/\" \. \$$1->$2() \. \"/g;
+ $docTemplate .= "${indent}print\"$line\";";
+ }
+ # else { $docTemplate .= "\n"; }
+ $literal = 0;
+ }
+ else {
+ if (substr( $_, 0, length( $indent ) ) eq $indent) {
+ substr( $_, 0, length( $indent ) ) = "";
+ }
+ chop;
+ s/\"/\\\"/g;
+ s/\$\((\w+)\.(\w+)\)/\" \. \$$1->$2() \. \"/g;
+ $_ = $indent . "print \"" . $_ . "\\n\";\n";
+ last;
+ }
+ }
+ else {
+ # Check for beginning of literal block.
+ if (s/^(\s*)\>\>//) {
+ $indent = $1;
+ $literal = 1;
+ }
+ elsif (s/^(\s*)(.*)\>\>//) {
+ $docTemplate .= "$indent$2";
+ $literal = 1;
+ }
+ else {
+ last;
+ }
+ }
+ }
+
+ $docTemplate .= $_;
+ }
+ close( FILE );
+ # print $docTemplate;
+}
+
+# Functions intended to be called from doc template file.
+
+# Open a new output file
+sub file {
+ my $mfile = $_[ 0 ];
+
+ open( STDOUT, ">$destPath$mfile" ) || die "Error writing to '$mfile'\n";
+}
+
+# return list of package objects
+sub packages {
+ my ($p, @r);
+ @r = ();
+
+ foreach $p (sort keys %packages) {
+ push @r, $packages{ $p };
+ }
+ return @r;
+}
+
+# return list of source files which have to-do lists
+sub todolistFiles {
+ my ($p, @r);
+ @r = ();
+
+ foreach $p (sort keys %todolist) {
+ push @r, $p;
+ }
+ return @r;
+}
+
+# return list of tab-delimited to-do-list texts.
+sub todolistEntries {
+ local $_ = $todolist{ $_[0] };
+ s/^\s+//; # Remove whitespace from beginning
+ s/\s+$/\n/; # Remove whitespace from end
+ return split( /\n/, $_ );
+}
+
+# Convert package name to URL.
+sub packageURL {
+ my $p = $_[0];
+
+ if ($p eq 'General') { $p = '.general'; }
+ if ($p eq '') { $p = '.general'; }
+
+ if (ref $packages{ $p }) {
+ return $packages{ $p }->url();
+ }
+ return 0;
+}
+
+# Get the see-also list for an object
+sub seealsoList {
+ my $self = shift;
+ my ($see, $name, $url, $p, @r);
+ @r = ();
+
+ if (defined ($self->{ 'see' })) {
+ foreach $_ (split(/\n/,$self->{ 'see' })) {
+
+ if (/^\<a\s+href/) { # if already an HREF.
+ $name = $_;
+ $url = 0;
+ }
+ elsif (/([^\#]*)\#(.*)/) { # If a package name is present
+ $url = &packageURL( $1 ) . '#' . $2;
+ $name = $2;
+ }
+ else {
+ $name = $_;
+ $url = "#$_";
+
+ # This doesn't appear to do anything - so I commented it. (james)
+ # Look up the package in the index and use it to construct the html filename.
+ #if (/^([^\:]*)\:\:(.*)/) {
+ # $className = ($1 eq '') ? '' : $classToPackage{ $1 };
+ # $p = $packageToFile{ $className };
+ # if ($p ne '') {
+ # $url = &packageURL( $1 ) . '#' . $_;
+ # }
+ #}
+ }
+
+ $url =~ s/^\:*//; # Remove leading colons from name
+ $url =~ s/::/-/g; # Replace :: with dash
+
+ my $entry = { 'name' => $name,
+ 'url' => $url };
+
+ bless $entry, DocReference;
+
+ push @r, $entry;
+ }
+ }
+ return @r;
+}
+
+sub removeApacheMacros {
+# print "removing from $_";
+ s|AP_DECLARE\((.*?)\)|$1|;
+}
+
+# Class for parsed package
+package PackageRecord;
+
+sub classes {
+ my $self = shift;
+ my $classes = $self->{ 'classes' };
+ return map $classes->{ $_ }, (sort keys %$classes);
+}
+
+sub globals {
+ my $self = shift;
+ my $globals = $self->{ 'globals' };
+ return map $globals->{ $_ }, (sort keys %$globals);
+}
+
+sub globalvars {
+ my $self = shift;
+ my $globals = $self->{ 'globals' };
+ my ($p, @r);
+ @r = ();
+
+ foreach $p (sort keys %$globals) {
+ my $m = $globals->{ $p };
+ if ($m->{ 'type' } ne 'func') { push @r, $m; }
+ }
+ return @r;
+}
+
+sub globalfuncs {
+ my $self = shift;
+ my $globals = $self->{ 'globals' };
+ my ($p, @r);
+ @r = ();
+
+ foreach $p (sort keys %$globals) {
+ my $m = $globals->{ $p };
+ if ($m->{ 'type' } eq 'func') { push @r, $m; }
+ }
+ return @r;
+}
+
+sub name {
+ my $self = shift;
+ return $self->{ 'name' };
+}
+
+sub url {
+ my $self = shift;
+ return "default-pkg.html" if ($self->{ 'name' } eq '.general');
+ return $self->{ 'name' } . '.html';
+}
+
+sub anchor {
+ my $self = shift;
+ my $url = $self->{ 'name' };
+ return $url;
+}
+
+# Class for parsed class
+package ClassRecord;
+
+sub keywords { return ${$_[0]}{ 'keywords' }; }
+sub author { return ${$_[0]}{ 'author' }; }
+sub version { return ${$_[0]}{ 'version' }; }
+sub name { return ${$_[0]}{ 'name' }; }
+sub longname { return ${$_[0]}{ 'longname' }; }
+sub fullname { return ${$_[0]}{ 'fullname' }; }
+sub scopename { return ${$_[0]}{ 'scopename' }; }
+sub sourcefile { return ${$_[0]}{ 'sourcefile' }; }
+#sub description { return &::processDescription( ${$_[0]}{ 'description' } ); }
+sub description { return ${$_[0]}{ 'description' }; }
+sub seealso { &::seealsoList( $_[0] ); }
+
+sub url {
+ my $self = shift;
+ return 0 unless $self->{ 'package' };
+ my $pname = ::packageURL( $self->{ 'package' } );
+ my $url = $self->{ 'uname' };
+ $url =~ s/::/-/g;
+ return "$pname#$url";
+}
+
+sub anchor {
+ my $self = shift;
+ my $url = $self->{ 'uname' };
+ $url =~ s/::/-/g;
+ return $url;
+}
+
+sub members {
+ my $self = shift;
+ my $members = $self->{ 'members' };
+ my ($p, @r);
+ @r = ();
+
+ foreach $p (sort keys %$members) {
+ push @r, $members->{ $p };
+ }
+ return @r;
+}
+
+sub membervars {
+ my $self = shift;
+ my $members = $self->{ 'members' };
+ my ($p, @r);
+ @r = ();
+
+ foreach $p (sort keys %$members) {
+ my $m = $members->{ $p };
+ if ($m->{ 'type' } ne 'func') { push @r, $m; }
+ }
+ return @r;
+}
+
+sub memberfuncs {
+ my $self = shift;
+ my $members = $self->{ 'members' };
+ my ($p, @r);
+ @r = ();
+
+ foreach $p (sort keys %$members) {
+ my $m = $members->{ $p };
+ if ($m->{ 'type' } eq 'func') { push @r, $m; }
+ }
+ return @r;
+}
+
+sub baseclasses {
+ my $self = shift;
+ my $bases = $self->{ 'bases' };
+ my ($p, $class, @r);
+ @r = ();
+
+ foreach $p (@$bases) {
+
+ unless ($class = $::classList{ $p }) {
+ # It's one we don't know about, so just make something up
+ $class = { 'name' => $p,
+ 'longname'=> "class $p",
+ 'fullname'=> "class $p",
+ 'scopename'=>"class $p",
+ 'uname' => $p,
+ 'members' => {} };
+
+ if ($::classToPackage{ $p }) {
+ $class->{ 'package' } = $::classToPackage{ $p };
+ }
+
+ bless $class, ClassRecord;
+ }
+ push @r, $class;
+ }
+ return @r;
+}
+
+sub subclasses {
+ my $self = shift;
+ my $subs;
+ my ($p, $class, @r);
+ @r = ();
+
+ if (defined ($self->{ 'subs' })) {
+ $subs = $self->{ 'subs' };
+ foreach $p (sort @$subs) {
+ $class = $::classList{ $p };
+ push @r, $class;
+ }
+ }
+ return @r;
+}
+
+# Class for parsed class member or global
+package MemberRecord;
+
+sub type { return ${$_[0]}{ 'type' }; }
+sub keywords { return ${$_[0]}{ 'keywords' }; }
+sub author { return ${$_[0]}{ 'author' }; }
+sub version { return ${$_[0]}{ 'version' }; }
+sub name { return ${$_[0]}{ 'name' }; }
+sub longname { return ${$_[0]}{ 'longname' }; }
+sub fullname { return ${$_[0]}{ 'fullname' }; }
+sub scopename { return ${$_[0]}{ 'scopename' }; }
+sub returnValue { return ${$_[0]}{ 'return' }; }
+sub sourcefile { return ${$_[0]}{ 'sourcefile' }; }
+sub description { return ${$_[0]}{ 'description' }; }
+sub seealso { &::seealsoList( $_[0] ); }
+
+sub url {
+ my $self = shift;
+ return 0 unless $self->{ 'package' };
+ my $pname = ::packageURL( $self->{ 'package' } );
+ my $url = $self->{ 'uname' };
+ $url =~ s/::/-/g;
+ return "$pname#$url";
+}
+
+sub anchor {
+ my $self = shift;
+ my $url = $self->{ 'uname' };
+ $url =~ s/::/-/g;
+ $url;
+}
+
+sub params {
+ my $self = shift;
+ my $params = $self->{ 'param' };
+ my @r;
+ @r = ();
+
+ return 0 unless ($params);
+
+ my @paramList = split( /\t/, $params );
+
+ for ($i = 1; $i < $#paramList; $i += 2) {
+ my $entry = { 'name' => $paramList[ $i ],
+ 'description' => $paramList[ $i + 1 ] };
+
+ bless $entry, ArgRecord;
+
+ push @r, $entry;
+ }
+ return @r;
+}
+
+sub exceptions {
+ my $self = shift;
+ my $params = $self->{ 'exception' };
+ my @r;
+ @r = ();
+
+ return 0 unless ($params);
+
+ my @paramList = split( /\t/, $params );
+
+ for ($i = 1; $i < $#paramList; $i += 2) {
+ my $entry = { 'name' => $paramList[ $i ],
+ 'description' => $paramList[ $i + 1 ] };
+
+ bless $entry, ArgRecord;
+
+ push @r, $entry;
+ }
+ return @r;
+}
+
+package ArgRecord;
+sub name { return ${$_[0]}{ 'name' }; }
+sub description { return ${$_[0]}{ 'description' }; }
+
+package DocReference;
+sub name { return ${$_[0]}{ 'name' }; }
+sub url { return ${$_[0]}{ 'url' }; }
diff --git a/build/scandoc_template.pl b/build/scandoc_template.pl
new file mode 100644
index 000000000..f260cd5d3
--- /dev/null
+++ b/build/scandoc_template.pl
@@ -0,0 +1,518 @@
+<<
+# Scandoc template file.
+#
+# This is an example set of templates that is designed to create several
+# different kinds of index files. It generates a "master index" which intended
+# for use with a frames browser; A "package index" which is the root page of
+# the index, and then "package files" containing documentation for all of the
+# classes within a single package.
+
+######################################################################
+
+## For quick and superficial customization,
+## simply change these variables
+
+$project_name = '[Apache Portable RunTime]';
+#$company_logo = '<img src="../images/ScanDocBig.jpg">'; # change this to an image tag.
+$copyright = '&copy 2000 [Apache Software Foundation]';
+$image_directory = "../images/";
+$bullet1_image = $image_directory . "ball1.gif";
+$bullet2_image = $image_directory . "ball2.gif";
+$bgcolor1 = "#FFFFFF";
+$bgcolor2 = "#FFFFFF";
+
+######################################################################
+
+## Begin generating frame index file.
+
+file "index.html";
+>><html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; iso-8859-1">
+ <title>$project_name</title>
+ </head>
+ <frameset cols="190,*">
+ <frame src="master.html" name="Master Index" noresize>
+ <frame src="packages.html" name="Documentation">
+ <noframes>
+ <body bgcolor="$bgcolor2" stylesrc="index.html">
+ <p>Some Documentation</p>
+ </body>
+ </noframes>
+ </frameset>
+</html>
+<<
+
+######################################################################
+
+## Begin generating master index file (left-hand frame).
+
+file "master.html";
+>><html>
+ <head>
+ <title>Master Index</title>
+ </head>
+ <body bgcolor="$bgcolor1" text=#0000ff link=#0020ff vlink=#0020ff>
+ <center><img src="${image_directory}ScanDocSmall.jpg" border="0" /></center>
+ <p>
+ <a href="packages.html" target="Documentation">Master Index</a>
+ </p>
+ <p>
+ <font size="2">
+ <nobr>
+<<
+
+## For each package, generate an index entry.
+
+foreach $p (packages()) {
+ $_ = $p->url;
+ s/\s/_/g;
+ >><a href="$_" target="Documentation"><b>$(p.name)</b></a><br>
+ <dir>
+ <<
+ foreach $e ($p->classes()) {
+ $_ = $e->url;
+ s/\s/_/g;
+ >><li><a href="$_" target="Documentation">$(e.fullname)</a>
+ <<
+ }
+ foreach $e ($p->globals()) {
+ $_ = $e->url;
+ s/\s/_/g;
+ >><li><a href="$_" target="Documentation">$(e.fullname)</a>
+ <<
+ }
+ >></dir><<
+}
+
+>>
+ <a href="to-do.html" target="Documentation"><b>To-Do List</b></a><br>
+ </nobr>
+ </font>
+ </p>
+ </body>
+</html>
+<<
+
+######################################################################
+
+## Begin generating package index file
+
+file "packages.html";
+>><html>
+ <head>
+ <title>$project_name -- Packages</title>
+ </head>
+ <body bgcolor="$bgcolor2">
+
+ <center>$company_logo
+ <h1>Documentation for $project_name</h1>
+ </center>
+ <h2>Package List</h2>
+<<
+
+## For each package, generate an index entry.
+
+foreach $p (packages()) {
+ $_ = $p->url;
+ s/\s/_/g;
+ >><a href = "$_">$(p.name)</a><br>
+ <<
+}
+
+>>
+ <p>
+ <hr size=4>
+ $copyright<br>
+ Generated by <a href="$scandocURL"><b>ScanDoc $majorVersion.$minorVersion</b></a><br>
+ Last Updated: $date<br>
+ </body>
+</html>
+
+<<
+
+######################################################################
+
+## Generate "To-do list"
+
+file "to-do.html";
+>><html>
+ <head>
+ <title>$project_name -- To-Do list</title>
+ </head>
+ <body bgcolor="$bgcolor2">
+
+ $company_logo
+
+ <h1>To-do list for $project_name</h1>
+<<
+
+if (&todolistFiles()) {
+ >><hr size=4><p>
+ <<
+ foreach $f (&todolistFiles()) {
+ my @m = &todolistEntries( $f );
+ if ($f =~ /([^\/]+)$/) { $f = $1; }
+ >><b>$f:</b><ul>
+ <<
+ foreach $text (@m) {
+ if ($text) {
+ print "<li>", &processDescription( $text ), "\n";
+ }
+ }
+ >></ul>
+ <<
+ }
+}
+
+>>
+ <hr size=4>
+ $copyright<br>
+ Generated by <a href="$scandocURL"><b>ScanDoc $majorVersion.$minorVersion</b></a><br>
+ Last Updated: $date<br>
+ </body>
+</html>
+<<
+
+######################################################################
+
+## Generate individual files for each package.
+
+my $p;
+foreach $p (packages()) {
+ $_ = $p->name;
+ s/\s/_/g;
+ file $_ . ".html";
+ >><html>
+ <head>
+ <title>$project_name -- $(p.name)</title>
+ </head>
+ <body bgcolor="$bgcolor2">
+ <center>
+ <font size=6><b>$project_name</b></font>
+ <hr size=4><p>
+ </center>
+
+ <h2>Package Name: $(p.name)</h2>
+ <b>
+<<
+
+## Generate class and member index at the top of the file.
+
+foreach $c ($p->classes()) {
+ $_ = $c->url;
+ s/\s/_/g;
+ >><h3><img src="$bullet1_image" width=18 height=17 align=texttop>
+ <a href="$_">$(c.fullname)</h3></a>
+ <ul>
+ <<
+ foreach $m ($c->members()) {
+ $_ = $m->url;
+ s/\s/_/g;
+ >><li><a href="$_">$(m.longname)</a>
+ <<
+ }
+ >></ul>
+ <<
+}
+
+>>
+</b>
+<<
+
+## Generate detailed class documentation
+foreach $c ($p->classes()) {
+ ## Output searchable keyword list
+ if ($c->keywords()) {
+ print "<!-- ", $c->keywords(), " -->\n";
+ }
+
+ >><hr size="4">
+ <a name="$(c.anchor)"></a>
+ <h1>$(c.fullname)</h1>
+ <table bgcolor="ffffff" border="0" cellspacing="4">
+ <tr>
+ <th align=center colspan=2>
+ </th>
+ </tr>
+ <<
+
+ # Output author tag
+ if ($c->author()) {
+ >><tr><th width=20% align=right>Author:</th><<
+ >><td>$(c.author)</td></tr><<
+ }
+
+ # Output package version
+ if ($c->version()) {
+ >><tr><th width=20% align=right>Version:</th><<
+ >><td>$(c.version)</td></tr><<
+ }
+
+ # Output Source file
+ if ($c->sourcefile()) {
+ >><tr><th width=20% align=right>Source:</th><<
+ >><td>$(c.sourcefile)</td></tr><<
+ }
+
+ # Output base class list
+ if ($c->baseclasses()) {
+ >><tr><th width=20% align=right>Base classes:</th>
+ <td><<
+ my @t = ();
+ foreach $b ($c->baseclasses()) {
+ my $name = $b->name();
+ if ($url = $b->url()) {
+ $_ = $url;
+ s/\s/_/g;
+ push @t, "<a href=\"$_\">$name</a>";
+ }
+ else { push @t, $name; }
+ }
+ print join( ', ', @t );
+ >></td></tr>
+ <<
+ }
+
+ # Output subclasses list
+ if ($c->subclasses()) {
+ >><tr><th width=20% align=right>Subclasses:</th>
+ <td><<
+ my @t = ();
+ foreach $s ($c->subclasses()) {
+ my $name = $s->name();
+ if ($url = $s->url()) {
+ $_ = $url;
+ s/\s/_/g;
+ push @t, "<a href=\"$_\">$name</a>";
+ }
+ else { push @t, $name; }
+ }
+ print join( ', ', @t );
+ >></td></tr><<
+ }
+
+ # Output main class description
+ >></tr>
+ </table>
+ <p>
+ <<
+ print &processDescription( $c->description() );
+
+ # Output "see also" information
+ if ($c->seealso()) {
+ >><p><dt><b>See Also</b><dd>
+ <<
+ my @r = ();
+ foreach $a ($c->seealso()) {
+ my $name = $a->name();
+ if ($url = $a->url()) {
+ $_ = $url;
+ s/\s/_/g;
+ push @r, "<a href=\"$_\">$name</a>";
+ }
+ else { push @r, $name; }
+ }
+ print join( ',', @r );
+ >><p>
+ <<
+ }
+
+ # Output class member index
+ if ($c->members()) {
+ print "<h2>Member Index</h2>\n";
+ print "<ul>";
+ foreach $m ($c->members()) {
+ $_ = $m->url;
+ s/\s/_/g;
+ >><li><a href="$_">$(m.fullname)</a>
+ <<
+ }
+ >></ul><<
+ }
+
+ # Output class member variable documentation
+ if ($c->membervars()) {
+ print "<h2>Class Variables</h2>\n";
+ print "<blockquote>\n";
+ foreach $m ($c->membervars()) { &variable( $m ); }
+ print "</blockquote>\n";
+ }
+
+ # Output class member function documentation
+ if ($c->memberfuncs()) {
+ print "<h2>Class Methods</h2>\n";
+ print "<blockquote>\n";
+ foreach $m ($c->memberfuncs()) { &function( $m ); }
+ print "</blockquote>\n";
+ }
+}
+
+# Output global variables
+if ($p->globalvars()) {
+ >><h2>Global Variables</h2>
+ <blockquote>
+ <<
+ foreach $m ($p->globalvars()) { &variable( $m ); }
+ print "</blockquote>\n";
+}
+
+# Output global functions
+if ($p->globalfuncs()) {
+ >><h2>Global Functions</h2>
+ <blockquote>
+ <<
+ foreach $m ($p->globalfuncs()) { &function( $m ); }
+ print "</blockquote>\n";
+}
+
+>>
+ <hr size=4>
+ $copyright<br>
+ Generated by <a href="$scandocURL"><b>ScanDoc $majorVersion.$minorVersion</b></a><br>
+ Last Updated: $date<br>
+ </body>
+</html>
+<<
+} # end of foreach (packages) loop
+
+######################################################################
+
+## Subroutine to generate documentation for a member function or global function
+
+sub function {
+ local ($f) = @_;
+
+ if ($f->keywords()) {
+ >><!-- $(f.keywords) -->
+ <<
+ }
+ >>
+ <a name="$(f.anchor)"></a>
+ <dl>
+ <dt>
+ <b><img src="$bullet2_image" width=19 height=17 align=texttop>$(f.fullname);</b>
+ <dd>
+ <<
+ print &processDescription( $f->description() );
+ >>
+ <p><dl>
+ <<
+ if ($f->params()) {
+ >>
+ <dt><b>Parameters</b><dd>
+ <table width="85%">
+ <<
+ foreach $a ($f->params()) {
+ >><tr valign=top><th align=right>
+ $(a.name)</th><td><<
+ print &processDescription( $a->description() );
+ >></td></tr>
+ <<
+ }
+ >></table>
+ <<
+ }
+
+ if ($f->returnValue()) {
+ >><dt><b>Return Value</b>
+ <dd><<
+ print &processDescription( $f->returnValue() );
+ >><p><<
+ }
+
+ if ($f->exceptions()) {
+ >><dt><b>Exceptions</b><dd>
+ <table width=85%><tr><td colspan=2><hr size=3></td></tr>
+ <<
+ foreach $a ($f->exceptions()) {
+ >><tr valign=top><th align=right>
+ $(a.name)</th><td><<
+ print &processDescription( $a->description() );
+ >></td></tr>
+ <<
+ }
+ >><tr><td colspan=2><hr size=3></td></tr></table>
+ <<
+ }
+
+ if ($f->seealso()) {
+ >><dt><b>See Also</b><dd>
+ <<
+ my @r = ();
+ foreach $a ($f->seealso()) {
+ my $name = $a->name();
+ if ($url = $a->url()) {
+ $_ = $url;
+ s/\s/_/g;
+ push @r, "<a href=\"$_\">$name</a>";
+ }
+ else { push @r, $name; }
+ }
+ print join( ',', @r );
+ >><p><<
+ }
+ >></dl></dl>
+ <<
+}
+
+######################################################################
+
+## Subroutine to generate documentation for a member variable or global variable.
+
+sub variable {
+ local ($v) = @_;
+
+ if ($v->keywords()) {
+ print "<!-- $(v.keywords) -->";
+ }
+
+ >>
+ <a name="$(v.name)"></a>
+ <dl><dt>
+ <b><img src="$bullet2_image" width=19 height=17 align=texttop>$(v.fullname);</b>
+ <dd>
+ <<print &processDescription( $v->description() );>>
+ <p><dl>
+ <<
+ if ($v->seealso()) {
+ >><dt><b>See Also</b><dd>
+ <<
+ $comma = 0;
+ foreach $a ($v->seealso()) {
+ $_ = $a->url;
+ s/\s/_/g;
+ if ($comma) { print ","; }
+ $comma = 1;
+ >><a href="$_">$(a.name)</a>
+ <<
+ }
+ >><p>
+ <<
+ }
+ >></dl></dl>
+ <<
+}
+
+######################################################################
+
+sub processDescription {
+ local ($_) = @_;
+
+ # handle HTML markup issues.
+ s/</&lt;/g;
+ s/>/&gt;/g;
+
+ s/^\s+//; # Remove whitespace from beginning
+ s/\s+$/\n/; # Remove whitespace from end
+ s/\n\n/<p>\n/g; # Replace multiple CR's with paragraph markers
+ s:\@heading(.*)\n:<p><h2>$1</h2>:; # Handle heading text
+
+ # Handle embedded image tags
+ s:\@caution:<p><img src=\"${image_directory}/caution.gif\" align=left>:;
+ s:\@warning:<p><img src=\"${image_directory}/warning.gif\" align=left>:;
+ s:\@bug:<p><img src=\"${image_directory}/bug.gif\">:;
+ s:\@tip:<p><img src=\"${image_directory}/tip.gif\">:;
+
+ return $_;
+}