#! /usr/bin/perl -w # # Script name: gatherHeaderDoc # Synopsis: Finds all HeaderDoc generated docs in an input # folder and creates a top-level HTML page to them # # Author: Matt Morse (matt@apple.com) # Last Updated: $Date: 2004/11/29 23:40:28 $ # # Copyright (c) 1999-2004 Apple Computer, Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # # This file contains Original Code and/or Modifications of Original Code # as defined in and that are subject to the Apple Public Source License # Version 2.0 (the 'License'). You may not use this file except in # compliance with the License. Please obtain a copy of the License at # http://www.opensource.apple.com/apsl/ and read it before using this # file. # # The Original Code and all software distributed under the License are # distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER # EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, # INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. # Please see the License for the specific language governing rights and # limitations under the License. # # @APPLE_LICENSE_HEADER_END@ # # $Revision: 1.8.4.39 $ ###################################################################### my $pathSeparator; my $isMacOS; my $uninstalledModulesPath; my $has_resolver; sub resolveLinks($); BEGIN { use FindBin qw ($Bin); if ($^O =~ /MacOS/i) { $pathSeparator = ":"; $isMacOS = 1; #$Bin seems to return a colon after the path on certain versions of MacPerl #if it's there we take it out. If not, leave it be #WD-rpw 05/09/02 ($uninstalledModulesPath = $FindBin::Bin) =~ s/([^:]*):$/$1/; } else { $pathSeparator = "/"; $isMacOS = 0; } $uninstalledModulesPath = "$FindBin::Bin"."$pathSeparator"."Modules"; } use strict; use Cwd; use File::Basename; use File::Find; use File::Copy; use lib $uninstalledModulesPath; $has_resolver = 1; eval "use HeaderDoc::LinkResolver qw (resolveLinks); 1" || do { $has_resolver = 0; }; # print "HR: $has_resolver\n"; if ($has_resolver) { print "LinkResolver will be used to resolve cross-references.\n"; } # Modules specific to gatherHeaderDoc use HeaderDoc::DocReference; use HeaderDoc::Utilities qw(findRelativePath safeName getAPINameAndDisc printArray printHash updateHashFromConfigFiles getHashFromConfigFile quote resolveLinks sanitize); $HeaderDoc::modulesPath = $INC{'HeaderDoc/Utilities.pm'}; $HeaderDoc::modulesPath =~ s/Utilities.pm$//s; # print "MP: ".$HeaderDoc::modulesPath."\n"; my $debugging = 1; ######################################## Design Overview ################################### # - We scan input directory for frameset files (index.html, by default). # - For each frameset file, we look for a special HTML comment (left by HeaderDoc) # that tell us the name of the header/class and the type (header or cppclass). # - We create a DocReference object to store this info, and also the path to the # frameset file. # - We run through array of DocRef objs and create a master TOC based on the info # - Finally, we visit each TOC file in each frameset and add a "[Top]" link # back to the master TOC. [This is fragile in the current implementation, since # we find TOCs based on searching for a file called "toc.html" in the frameset dir.] # ########################## Setup from Configuration File ####################### my $localConfigFileName = "headerDoc2HTML.config"; my $preferencesConfigFileName = "com.apple.headerDoc2HTML.config"; my $homeDir; my $usersPreferencesPath; my $systemPreferencesPath; #added WD-rpw 07/30/01 to support running on MacPerl #modified WD-rpw 07/01/02 to support the MacPerl 5.8.0 if ($^O =~ /MacOS/i) { eval { require "FindFolder.pl"; $homeDir = MacPerl::FindFolder("D"); #D = Desktop. Arbitrary place to put things $usersPreferencesPath = MacPerl::FindFolder("P"); #P = Preferences }; if ($@) { import Mac::Files; $homeDir = Mac::Files::FindFolder(kOnSystemDisk(), kDesktopFolderType()); $usersPreferencesPath = Mac::Files::FindFolder(kOnSystemDisk(), kPreferencesFolderType()); } $systemPreferencesPath = $usersPreferencesPath; } else { $homeDir = (getpwuid($<))[7]; $usersPreferencesPath = $homeDir.$pathSeparator."Library".$pathSeparator."Preferences"; $systemPreferencesPath = "/Library/Preferences"; } my $CWD = getcwd(); my @configFiles = ($systemPreferencesPath.$pathSeparator.$preferencesConfigFileName, $usersPreferencesPath.$pathSeparator.$preferencesConfigFileName, $Bin.$pathSeparator.$localConfigFileName, $CWD.$pathSeparator.$localConfigFileName); # ($Bin.$pathSeparator.$localConfigFileName, $usersPreferencesPath.$pathSeparator.$preferencesConfigFileName); # default configuration, which will be modified by assignments found in config files. # The default values listed in this hash must be the same as those in the identical # hash in headerDoc2HTML--so that links between the frameset and the masterTOC work. my %config = ( defaultFrameName => "index.html", masterTOCName => "MasterTOC.html" ); %config = &updateHashFromConfigFiles(\%config,\@configFiles); my $framesetFileName; my $masterTOCFileName; my $bookxmlname = ""; my @TOCTemplateList = (); my @TOCNames = (); my $framework = ""; my $frameworkShortName = ""; my $landingPageUID = ""; my $stripDotH = 0; my $gather_functions = 0; my $gather_types = 0; my $gather_globals_and_constants = 0; my $gather_man_pages = 0; my $apiUIDPrefix = "apple_ref"; if (defined $config{"defaultFrameName"}) { $framesetFileName = $config{"defaultFrameName"}; } if (defined $config{"apiUIDPrefix"}) { $apiUIDPrefix = $config{"apiUIDPrefix"}; } if (defined $config{"masterTOCName"}) { $masterTOCFileName = $config{"masterTOCName"}; } if (defined $config{"stripDotH"}) { $stripDotH = $config{"stripDotH"}; } if (defined $config{"TOCTemplateFile"}) { my $TOCTemplateFile = $config{"TOCTemplateFile"}; my $oldRecSep = $/; undef $/; # read in files as strings my @filelist = split(/\s/, $TOCTemplateFile); foreach my $file (@filelist) { print "Searching for $file\n"; my @templateFiles = ($Bin.$pathSeparator.$file, $usersPreferencesPath.$pathSeparator.$file, $file); my $TOCTemplate = ""; my $found = 0; foreach my $filename (@templateFiles) { if (open(TOCFILE, "<$filename")) { $TOCTemplate = ; close(TOCFILE); $found = 1; } } if (!$found) { die("Template file $file not found.\n"); } push(@TOCTemplateList, $TOCTemplate); push(@TOCNames, basename($file)); if (!$gather_types && $TOCTemplate =~ /\$\$\s*typelist/) { $gather_types = 1; } if (!$gather_globals_and_constants && $TOCTemplate =~ /\$\$\s*datalist/) { $gather_globals_and_constants = 1; } if (!$gather_functions && $TOCTemplate =~ /\$\$\s*functionlist/) { $gather_functions = 1; } if (!$gather_man_pages && $TOCTemplate =~ /\$\$\s*manpagelist/) { $gather_man_pages = 1; } } $/ = $oldRecSep; } my $useBreadcrumbs = 0; if (defined $config{"useBreadcrumbs"}) { $useBreadcrumbs = $config{"useBreadcrumbs"}; } $GHD::bgcolor = "#ffffff"; if (!scalar(@TOCTemplateList)) { my $TOCTemplate = default_template(); push(@TOCTemplateList, $TOCTemplate); push(@TOCNames, "masterTOC.html") } # print "GatherFunc: $gather_functions\n"; # print "TT: $TOCTemplate\n"; ########################## Input Folder and Files ####################### my @inputFiles; my @contentFiles; my $inputDir; if (($#ARGV == 0 || $#ARGV == 1 || $#ARGV == 2) && (-d $ARGV[0])) { $inputDir = $ARGV[0]; if ($#ARGV) { $masterTOCFileName = $ARGV[1]; } if ($#ARGV > 1) { $bookxmlname = $ARGV[2]; } if ($^O =~ /MacOS/i) { find(\&getFiles, $inputDir); $inputDir =~ s/([^:]*):$/$1/; #WD-rpw 07/01/02 } else { $inputDir =~ s|(.*)/$|$1|; # get rid of trailing slash, if any if ($inputDir !~ /^\//) { # not absolute path -- !!! should check for ~ my $cwd = cwd(); $inputDir = $cwd.$pathSeparator.$inputDir; } &find({wanted => \&getFiles, follow => 1}, $inputDir); } } else { die "You must specify a single input directory for processing.\n"; } unless (@inputFiles) { print "No valid input files specified. \n\n";}; sub getFiles { my $filePath = $File::Find::name; my $fileName = $_; my $localDebug = 0; my $basePath = dirname($filePath); my $dirName = basename($basePath); print "$fileName ($filePath): " if ($localDebug); if ($fileName =~ /$framesetFileName/) { print "HTML frameset\n" if ($localDebug); push(@inputFiles, $filePath); } elsif ($dirName =~ /^(man|cat)[\w\d]+$/ && $gather_man_pages) { print "Man Page\n" if ($localDebug); push(@inputFiles, $filePath); push(@contentFiles, $filePath); } elsif ($fileName =~ /Constants\.html$/ && $gather_globals_and_constants) { print "Constants\n" if ($localDebug); push(@inputFiles, $filePath); push(@contentFiles, $filePath); } elsif ($fileName =~ /Vars\.html$/ && $gather_globals_and_constants) { print "Vars\n" if ($localDebug); push(@inputFiles, $filePath); push(@contentFiles, $filePath); } elsif ($fileName =~ /DataTypes\.html$/ && $gather_types) { print "DataTypes\n" if ($localDebug); push(@inputFiles, $filePath); push(@contentFiles, $filePath); } elsif ($fileName =~ /Structs\.html$/ && $gather_types) { print "Structs\n" if ($localDebug); push(@inputFiles, $filePath); push(@contentFiles, $filePath); } elsif ($fileName =~ /Enums\.html$/ && $gather_types) { print "Enums\n" if ($localDebug); push(@inputFiles, $filePath); push(@contentFiles, $filePath); } elsif ($fileName =~ /Methods\.html$/ && $gather_functions) { print "Methods\n" if ($localDebug); push(@inputFiles, $filePath); push(@contentFiles, $filePath); } elsif ($fileName =~ /Functions\.html$/ && $gather_functions) { print "Functions\n" if ($localDebug); push(@inputFiles, $filePath); push(@contentFiles, $filePath); } elsif ($fileName =~ /doc\.html$/) { print "Framework Documentation\n" if ($localDebug); # Framework (maybe) push(@inputFiles, $filePath); push(@contentFiles, $filePath); } elsif ($fileName !~ /toc\.html$/) { print "Other Content\n" if ($localDebug); # Don't push the TOCs. push(@contentFiles, $filePath); } else { print "toc.\n" if ($localDebug); } } ########################## Find HeaderDoc Comments ####################### my @headerFramesetRefs; my @dataFramesetRefs; my @typeFramesetRefs; my @classFramesetRefs; my @manpageFramesetRefs; my @categoryFramesetRefs; my @protocolFramesetRefs; my @functionRefs; my $frameworkabstract = ""; my $frameworkdiscussion = ""; my $oldRecSep = $/; undef $/; # read in files as strings my $localDebug = 0; my %groups = (); $groups{" "}=""; $| = 1; print "Processing..."; foreach my $file (@inputFiles) { open (INFILE, "<$file") || die "Can't open $file: $!\n"; my $fileString = ; close INFILE; my $fileStringCopy = $fileString; while ($fileStringCopy =~ s/<\!--\s+(headerDoc\s*=.*?)-->(.*)/$2/s) { my $fullComment = $1; my $tail = $2; my $inDiscussion = 0; my $inAbstract = 0; my @stockpairs = split(/;/, $fullComment); my @pairs = (); my $temp = ""; foreach my $stockpair (@stockpairs) { if (length($temp)) { $temp .= $stockpair; if ($temp !~ /\\$/) { push(@pairs, $temp); $temp = ""; } } else { if ($stockpair =~ /\\$/) { $temp = $stockpair; } else { push(@pairs, $stockpair); $temp = ""; } } } my $docRef = HeaderDoc::DocReference->new; $docRef->path($file); # print "PATH: $file\n"; print "."; foreach my $pair (@pairs) { my ($key, $value) = split(/=/, $pair, 2); $key =~ s/^\s+|\s+$//; $value =~ s/^\s+|\s+$//; SWITCH: { ($key =~ /indexgroup/) && do { my $group = $value; $group =~ s/^\s*//sg; $group =~ s/\s*$//sg; $group =~ s/\\;/;/sg; $docRef->group($group); $groups{$group}=1; # print "SAW $group\n"; }; ($key =~ /headerDoc/) && do { $docRef->type($value); if ($value =~ /frameworkdiscussion/) { $inDiscussion = 1; } if ($value =~ /frameworkabstract/) { $inAbstract = 1; } last SWITCH; }; ($key =~ /shortname/) && do { $docRef->shortname($value); last SWITCH; }; ($key =~ /uid/) && do { $docRef->uid($value); }; ($key =~ /name/) && do { $docRef->name($value); if ($inDiscussion && $value =~ /start/) { $frameworkdiscussion = $tail; $frameworkdiscussion =~ s/ '$tocFile' doesn't exist!\n"; print "Cannot add [top] link for frameset doc reference:\n"; print " name: $name\n"; print " type: $type\n"; print " path: $path\n"; } } if ($useBreadcrumbs) { foreach my $file (@contentFiles) { # print "FILE: $file\n"; if (-e "$file" && ! -d "$file" ) { my $oldRecSep = $/; undef $/; # read in file as string open(INFILE, "<$file") || die "Can't read file $file.\n"; my $fileString = ; close INFILE; $/ = $oldRecSep; my $uniqueMarker = "headerDoc=\"topLink\""; # if ($fileString !~ /$uniqueMarker/) { # we haven't been here before if (length($framework)) { my $relPathToMasterTOC = &findRelativePath($file, $masterTOC); my $breadcrumb = "$framework"; $fileString =~ s/.*?/$breadcrumb/i; open (OUTFILE, ">$file") || die "Can't write file $file.\n"; print OUTFILE $fileString; close (OUTFILE); } else { warn "No framework (.hdoc) file found and breadcrumb specified. Breadcrumbs will\nnot be inserted.\n"; } # } } } } } sub getLinkToFramesetFrom { my $masterTOCFile = shift; my $dest = shift; my $name = shift; my $linkString; my $relPath = &findRelativePath($masterTOCFile, $dest); $linkString = "$name
\n"; return $linkString; } sub getLinkToFunctionFrom { my $masterTOCFile = shift; my $dest = shift; my $name = shift; my $uid = shift; my $linkString; my $relPath = &findRelativePath($masterTOCFile, $dest); my $noClassName = $name; $noClassName =~ s/.*\:\://s; my $urlname = sanitize($noClassName); my $lp = ""; if ($uid && length($uid)) { $urlname = $uid; $lp = " logicalPath=\"$uid\""; } $linkString = "$name
\n"; return $linkString; } sub objName { # for sorting objects by their names uc($a->name()) cmp uc($b->name()); } sub default_template { my $template = "\n"; $template .= "\n\n \$\$title\@\@\n \n\n

\$\$framework\@\@ Documentation



\n"; $template .= "

\$\$frameworkdiscussion\@\@

"; $template .= "\$\$headersection\@\@

Headers

\n
\n\$\$headerlist cols=2 order=down atts=border=0 width=\"80%\"\@\@\n
\$\$/headersection\@\@\n"; $template .= "\$\$classsection\@\@

Classes

\n
\n\$\$classlist cols=2 order=down atts=border=0 width=\"80%\"\@\@\n
\$\$/classsection\@\@\n"; $template .= "\$\$categorysection\@\@

Categories

\n
\n\$\$categorylist cols=2 order=down atts=border=0 width=\"80%\"\@\@\n
\$\$/categorysection\@\@\n"; $template .= "\$\$protocolsection\@\@

Protocols

\n
\n\$\$protocollist cols=2 order=down atts=border=0 width=\"80%\"\@\@\n
\$\$/protocolsection\@\@\n"; $template .= "\$\$functionsection\@\@

Functions

\n
\n\$\$functionlist cols=2 order=down atts=border=0 width=\"80%\"\@\@\n
\$\$/functionsection\@\@\n"; $template .= "\$\$typesection\@\@

Data Types

\n
\n\$\$typelist cols=2 order=down atts=border=0 width=\"80%\"\@\@\n
\$\$/typesection\@\@\n"; $template .= "\$\$datasection\@\@

Globals and Constants

\n
\n\$\$datalist cols=2 order=down atts=border=0 width=\"80%\"\@\@\n
\$\$/datasection\@\@\n"; $template .= "\n\n"; return $template; } sub genTable { my $inputstring = shift; my $settings = shift; my $groupname = shift; my $ncols = 0; my $order = "down"; my $attributes = "border=0 width=\"100%\""; my $localDebug = 0; my $ngroups = scalar(keys(%groups)); my $groupnamestring = ""; if ($groupname =~ /\S/) { $groupnamestring = "

$groupname

\n"; } my $groupheadstring = "
\n"; my $grouptailstring = "
\n"; if (!$ngroups) { $groupheadstring = ""; $grouptailstring = ""; } $settings =~ s/^\s*(\w+list)\s*//; my $name = $1; if ($settings =~ s/^cols=(\d+)\s+//) { $ncols = $1; } if ($settings =~ s/^order=(\w+)\s+//) { $order = $1; if (!$ncols) { $ncols = 1; } } if ($settings =~ s/^atts=(\w+)\s+//) { $attributes = $1; if (!$ncols) { $ncols = 1; } } if ($ncols) { my @lines = split(/\n/, $inputstring); my $nlines = scalar(@lines); if (!$nlines) { return ""; } my @columns = (); my $loopindex = $ncols; while ($loopindex--) { my @column = (); push(@columns, \@column); } my $curcolumn = 0; my $curline = 0; my $lines_per_column = int(($nlines + $ncols - 1) / $ncols); # ceil(nlines/ncols) my $blanks = ($lines_per_column * $ncols) - $nlines; $nlines += $blanks; while ($blanks) { push(@lines, ""); $blanks--; } warn "NLINES: $nlines\n" if ($localDebug); warn "Lines per column: $lines_per_column\n" if ($localDebug); foreach my $line (@lines) { warn "columns[$curcolumn] : adding line\n" if ($localDebug); my $columnref = $columns[$curcolumn]; push(@{$columnref}, $line); $curline++; if ($order eq "across") { $curcolumn = ($curcolumn + 1) % $ncols; } elsif ($curline >= $lines_per_column) { $curline = 0; $curcolumn++; } } if ($localDebug) { $loopindex = 0; while ($loopindex < $ncols) { warn "Column ".$loopindex.":\n"; foreach my $line (@{$columns[$loopindex]}) { warn "$line\n"; } $loopindex++; } } my $outstring = ""; $curline = 0; $curcolumn = 0; my $currow = 0; my $first = 1; while ($curline < $nlines) { if (!$curcolumn) { if ($first) { $first = 0; $outstring .= ""; } else { $outstring .= "\n"; $currow++; } } my $line = ${$columns[$curcolumn]}[$currow]; $outstring .= "\n"; $curline++; $curcolumn = ($curcolumn + 1) % $ncols; } $outstring .= "
$line
\n"; return $groupnamestring.$groupheadstring.$outstring.$grouptailstring; } else { return $groupnamestring.$groupheadstring.$inputstring.$grouptailstring; } }