diff options
author | fielding <fielding@13f79535-47bb-0310-9956-ffa450edef68> | 2001-02-18 13:36:15 +0000 |
---|---|---|
committer | fielding <fielding@13f79535-47bb-0310-9956-ffa450edef68> | 2001-02-18 13:36:15 +0000 |
commit | 8e603d3b136118876d73b6f4658346a0e5279611 (patch) | |
tree | 874549ae9173dd1849582aa36147f063062f6c53 | |
parent | 5762abb27eeaf7449fcf5f71d95860e8cb98e2a9 (diff) | |
download | libapr-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-x | build/MakeEtags | 39 | ||||
-rwxr-xr-x | build/PrintPath | 116 | ||||
-rw-r--r-- | build/cvstodsp5.pl | 43 | ||||
-rw-r--r-- | build/dsp5tocvs.pl | 46 | ||||
-rw-r--r-- | build/make_export.awk | 54 | ||||
-rwxr-xr-x | build/mkdep.sh | 12 | ||||
-rwxr-xr-x | build/mkdir.sh | 35 | ||||
-rw-r--r-- | build/rules.mk.in | 168 | ||||
-rwxr-xr-x | build/scandoc.pl | 1339 | ||||
-rw-r--r-- | build/scandoc_template.pl | 518 |
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 = "<"; + $nest = 1; + + for (;;) { + if (&matchRAngle) { $nest++; $args .= "<"; } + elsif (&matchLAngle) { + $nest--; + $args .= ">"; + 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/\&/\&/g; + $args =~ s/\&/\&/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/\&/\&/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 = '© 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/</</g; + s/>/>/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 $_; +} |