fielding 01/02/18 05:36:15 Added: build MakeEtags PrintPath cvstodsp5.pl dsp5tocvs.pl make_export.awk mkdep.sh mkdir.sh rules.mk.in scandoc.pl scandoc_template.pl Log: 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 Revision Changes Path 1.1 apr/build/MakeEtags Index: MakeEtags =================================================================== #!/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 - 1.1 apr/build/PrintPath Index: PrintPath =================================================================== #!/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 </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 1.1 apr/build/cvstodsp5.pl Index: cvstodsp5.pl =================================================================== 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; } } } 1.1 apr/build/dsp5tocvs.pl Index: dsp5tocvs.pl =================================================================== 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; } } } 1.1 apr/build/make_export.awk Index: make_export.awk =================================================================== # 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) } 1.1 apr/build/mkdep.sh Index: mkdep.sh =================================================================== #!/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 1.1 apr/build/mkdir.sh Index: mkdir.sh =================================================================== #!/bin/sh ## ## mkdir.sh -- make directory hierarchy ## ## Based on `mkinstalldirs' from Noah Friedman ## 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 ## # # 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 1.1 apr/build/rules.mk.in Index: rules.mk.in =================================================================== # ==================================================================== # 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 # . # # # 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 1.1 apr/build/scandoc.pl Index: scandoc.pl =================================================================== #!/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 -p -t -d= [ ... ]\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 ) && ($_ = )) { 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*)$/ && ($_ = )) {$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 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 .= $_; $_ = ; $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 .= $_; $_ = ; $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 () { $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 () { last if (/END/); # if we found a code entry. for (;;) { &expandTabs( $_ ); if ($literal) { # Check for beginning of code block. if (s/^(.*)\<\$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 (/^\ $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' }; } 1.1 apr/build/scandoc_template.pl Index: scandoc_template.pl =================================================================== << # 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 = ''; # 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"; >> $project_name <body bgcolor="$bgcolor2" stylesrc="index.html"> <p>Some Documentation</p> </body> << ###################################################################### ## Begin generating master index file (left-hand frame). file "master.html"; >> Master Index

Master Index

<< ## For each package, generate an index entry. foreach $p (packages()) { $_ = $p->url; s/\s/_/g; >>$(p.name)

<< foreach $e ($p->classes()) { $_ = $e->url; s/\s/_/g; >>
  • $(e.fullname) << } foreach $e ($p->globals()) { $_ = $e->url; s/\s/_/g; >>
  • $(e.fullname) << } >>
  • << } >> To-Do List

    << ###################################################################### ## Begin generating package index file file "packages.html"; >> $project_name -- Packages
    $company_logo

    Documentation for $project_name

    Package List

    << ## For each package, generate an index entry. foreach $p (packages()) { $_ = $p->url; s/\s/_/g; >>$(p.name)
    << } >>


    $copyright
    Generated by ScanDoc $majorVersion.$minorVersion
    Last Updated: $date
    << ###################################################################### ## Generate "To-do list" file "to-do.html"; >> $project_name -- To-Do list $company_logo

    To-do list for $project_name

    << if (&todolistFiles()) { >>

    << foreach $f (&todolistFiles()) { my @m = &todolistEntries( $f ); if ($f =~ /([^\/]+)$/) { $f = $1; } >>$f:

      << foreach $text (@m) { if ($text) { print "
    • ", &processDescription( $text ), "\n"; } } >>
    << } } >>
    $copyright
    Generated by ScanDoc $majorVersion.$minorVersion
    Last Updated: $date
    << ###################################################################### ## Generate individual files for each package. my $p; foreach $p (packages()) { $_ = $p->name; s/\s/_/g; file $_ . ".html"; >> $project_name -- $(p.name)
    $project_name

    Package Name: $(p.name)

    << ## Generate class and member index at the top of the file. foreach $c ($p->classes()) { $_ = $c->url; s/\s/_/g; >>

    $(c.fullname)

      << foreach $m ($c->members()) { $_ = $m->url; s/\s/_/g; >>
    • $(m.longname) << } >>
    << } >>
    << ## Generate detailed class documentation foreach $c ($p->classes()) { ## Output searchable keyword list if ($c->keywords()) { print "\n"; } >>

    $(c.fullname)

    << # Output author tag if ($c->author()) { >><< >><< } # Output package version if ($c->version()) { >><< >><< } # Output Source file if ($c->sourcefile()) { >><< >><< } # Output base class list if ($c->baseclasses()) { >> << } # Output subclasses list if ($c->subclasses()) { >><< } # Output main class description >>
    Author:$(c.author)
    Version:$(c.version)
    Source:$(c.sourcefile)
    Base classes: << my @t = (); foreach $b ($c->baseclasses()) { my $name = $b->name(); if ($url = $b->url()) { $_ = $url; s/\s/_/g; push @t, "$name"; } else { push @t, $name; } } print join( ', ', @t ); >>
    Subclasses: << my @t = (); foreach $s ($c->subclasses()) { my $name = $s->name(); if ($url = $s->url()) { $_ = $url; s/\s/_/g; push @t, "$name"; } else { push @t, $name; } } print join( ', ', @t ); >>

    << print &processDescription( $c->description() ); # Output "see also" information if ($c->seealso()) { >>

    See Also
    << my @r = (); foreach $a ($c->seealso()) { my $name = $a->name(); if ($url = $a->url()) { $_ = $url; s/\s/_/g; push @r, "$name"; } else { push @r, $name; } } print join( ',', @r ); >>

    << } # Output class member index if ($c->members()) { print "

    Member Index

    \n"; print "
      "; foreach $m ($c->members()) { $_ = $m->url; s/\s/_/g; >>
    • $(m.fullname) << } >>
    << } # Output class member variable documentation if ($c->membervars()) { print "

    Class Variables

    \n"; print "
    \n"; foreach $m ($c->membervars()) { &variable( $m ); } print "
    \n"; } # Output class member function documentation if ($c->memberfuncs()) { print "

    Class Methods

    \n"; print "
    \n"; foreach $m ($c->memberfuncs()) { &function( $m ); } print "
    \n"; } } # Output global variables if ($p->globalvars()) { >>

    Global Variables

    << foreach $m ($p->globalvars()) { &variable( $m ); } print "
    \n"; } # Output global functions if ($p->globalfuncs()) { >>

    Global Functions

    << foreach $m ($p->globalfuncs()) { &function( $m ); } print "
    \n"; } >>
    $copyright
    Generated by ScanDoc $majorVersion.$minorVersion
    Last Updated: $date
    << } # end of foreach (packages) loop ###################################################################### ## Subroutine to generate documentation for a member function or global function sub function { local ($f) = @_; if ($f->keywords()) { >> << } >>
    $(f.fullname);
    << print &processDescription( $f->description() ); >>

    << if ($f->params()) { >>
    Parameters
    << foreach $a ($f->params()) { >> << } >>
    $(a.name)<< print &processDescription( $a->description() ); >>
    << } if ($f->returnValue()) { >>
    Return Value
    << print &processDescription( $f->returnValue() ); >>

    << } if ($f->exceptions()) { >>

    Exceptions
    << foreach $a ($f->exceptions()) { >> << } >>

    $(a.name)<< print &processDescription( $a->description() ); >>

    << } if ($f->seealso()) { >>
    See Also
    << my @r = (); foreach $a ($f->seealso()) { my $name = $a->name(); if ($url = $a->url()) { $_ = $url; s/\s/_/g; push @r, "$name"; } else { push @r, $name; } } print join( ',', @r ); >>

    << } >>

    << } ###################################################################### ## Subroutine to generate documentation for a member variable or global variable. sub variable { local ($v) = @_; if ($v->keywords()) { print ""; } >>
    $(v.fullname);
    <description() );>>

    << if ($v->seealso()) { >>
    See Also
    << $comma = 0; foreach $a ($v->seealso()) { $_ = $a->url; s/\s/_/g; if ($comma) { print ","; } $comma = 1; >>$(a.name) << } >>

    << } >>

    << } ###################################################################### sub processDescription { local ($_) = @_; # handle HTML markup issues. s//>/g; s/^\s+//; # Remove whitespace from beginning s/\s+$/\n/; # Remove whitespace from end s/\n\n/

    \n/g; # Replace multiple CR's with paragraph markers s:\@heading(.*)\n:

    $1

    :; # Handle heading text # Handle embedded image tags s:\@caution:

    :; s:\@warning:

    :; s:\@bug:

    :; s:\@tip:

    :; return $_; }