#!/usr/bin/perl =begin nd Script: NaturalDocs ___________________________________________________________________________ Version 1.4 Copyright (C) 2003-2008 Greg Valure http://www.naturaldocs.org About: License Licensed under the GNU General Public License This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, visit http://www.gnu.org/licenses/gpl.txt or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. Topic: Code Conventions - Every package function is called with an arrow operator. It's needed for inheritance in some places, and consistency when it's not. - No constant will ever be zero or undef. Those are reserved so any piece of code can allow a "none of the above" option and not worry about conflicts with an existing value. - Existence hashes are hashes where the value doesn't matter. It acts more as a set, where the existence of the key is the significant part. Topic: File Format Conventions - All integers appear in big-endian format. So a UInt16 should be handled with a 'n' in pack and unpack, not with a 'S'. - AString16's are a big-endian UInt16 followed by that many ASCII characters. A null-terminator is not stored. - If a higher-level type is described in a file format, that means the loading and saving format is handled by that package. For example, if you see in the format, that means ToBinaryFile()> and FromBinaryFile()> are used to manipulate it, and the underlying format should be treated as opaque. =cut use strict; use integer; use 5.005; # When File::Spec was included by default use English '-no_match_vars'; use FindBin; use lib "$FindBin::RealBin/Modules"; sub INIT { # This function is just here so that when I start the debugger, it doesn't open a new file. Normally it would jump to an INIT # function in some other file since that's the first piece of code to execute. }; use NaturalDocs::Constants; use NaturalDocs::Version; use NaturalDocs::File; use NaturalDocs::Error; use NaturalDocs::ConfigFile; use NaturalDocs::BinaryFile; use NaturalDocs::StatusMessage; use NaturalDocs::SymbolString; use NaturalDocs::ReferenceString; use NaturalDocs::NDMarkup; use NaturalDocs::Settings; use NaturalDocs::Topics; use NaturalDocs::Languages; use NaturalDocs::Project; use NaturalDocs::Menu; use NaturalDocs::SymbolTable; use NaturalDocs::ClassHierarchy; use NaturalDocs::SourceDB; use NaturalDocs::ImageReferenceTable; use NaturalDocs::Parser; use NaturalDocs::Builder; ############################################################################### # # Group: Basic Types # # Types used throughout the program. As Perl is a weakly-typed language unless you box things into objects, these types are # for documentation purposes and are not enforced. # # # Type: FileName # # A string representing the absolute, platform-dependent path to a file. Relative file paths are no longer in use anywhere in the # program. All path manipulation should be done through . # # # Type: VersionInt # # A comparable integer representing a version number. Converting them to and from text and binary should be handled by # . # # # Type: SymbolString # # A scalar which encodes a normalized array of identifier strings representing a full or partially-resolved symbol. All symbols # must be retrieved from plain text via FromText()> so that the separation and normalization is # always consistent. SymbolStrings are comparable via string compare functions and are sortable. # # # Type: ReferenceString # # All the information about a reference that makes it unique encoded into a string. This includes the of the # reference, the scope it appears in, the scope it has access to via "using", and the # . This is done because if any of those parameters change, it needs to be treated as a completely separate # reference. # ############################################################################### # Group: Support Functions # General functions that are used throughout the program, and that don't really fit anywhere else. # # Function: StringCompare # # Compares two strings so that the result is good for proper sorting. A proper sort orders the characters as # follows: # # - End of string. # - Whitespace. Line break-tab-space. # - Symbols, which is anything not included in the other entries. # - Numbers, 0-9. # - Letters, case insensitive except to break ties. # # If you use cmp instead of this function, the result would go by ASCII/Unicode values which would place certain symbols # between letters and numbers instead of having them all grouped together. Also, you would have to choose between case # sensitivity or complete case insensitivity, in which ties are broken arbitrarily. # # Returns: # # Like cmp, it returns zero if A and B are equal, a positive value if A is greater than B, and a negative value if A is less than B. # sub StringCompare #(a, b) { my ($a, $b) = @_; if (!defined $a) { if (!defined $b) { return 0; } else { return -1; }; } elsif (!defined $b) { return 1; }; my $translatedA = lc($a); my $translatedB = lc($b); $translatedA =~ tr/\n\r\t 0-9a-z/\x01\x02\x03\x04\xDB-\xFE/; $translatedB =~ tr/\n\r\t 0-9a-z/\x01\x02\x03\x04\xDB-\xFE/; my $result = $translatedA cmp $translatedB; if ($result == 0) { # Break the tie by comparing their case. Lowercase before uppercase. # If statement just to keep everything theoretically kosher, even though in practice we don't need this. if (ord('A') > ord('a')) { return ($a cmp $b); } else { return ($b cmp $a); }; } else { return $result; }; }; # # Function: ShortenToMatchStrings # # Compares two arrayrefs and shortens the first array to only contain shared entries. Assumes all entries are strings. # # Parameters: # # sharedArrayRef - The arrayref that will be shortened to only contain common elements. # compareArrayRef - The arrayref to match. # sub ShortenToMatchStrings #(sharedArrayRef, compareArrayRef) { my ($sharedArrayRef, $compareArrayRef) = @_; my $index = 0; while ($index < scalar @$sharedArrayRef && $index < scalar @$compareArrayRef && $sharedArrayRef->[$index] eq $compareArrayRef->[$index]) { $index++; }; if ($index < scalar @$sharedArrayRef) { splice(@$sharedArrayRef, $index); }; }; # # Function: XChomp # # A cross-platform chomp function. Regular chomp fails when parsing Windows-format line breaks on a Unix platform. It # leaves the /r on, which screws everything up. This does not. # # Parameters: # # lineRef - A *reference* to the line to chomp. # sub XChomp #(lineRef) { my $lineRef = shift; $$lineRef =~ s/[\n\r]+$//; }; # # Function: FindFirstSymbol # # Searches a string for a number of symbols to see which appears first. # # Parameters: # # string - The string to search. # symbols - An arrayref of symbols to look for. # index - The index to start at, if any. # # Returns: # # The array ( index, symbol ). # # index - The index the first symbol appears at, or -1 if none appear. # symbol - The symbol that appeared, or undef if none. # sub FindFirstSymbol #(string, symbols, index) { my ($string, $symbols, $index) = @_; if (!defined $index) { $index = 0; }; my $lowestIndex = -1; my $lowestSymbol; foreach my $symbol (@$symbols) { my $testIndex = index($string, $symbol, $index); if ($testIndex != -1 && ($lowestIndex == -1 || $testIndex < $lowestIndex)) { $lowestIndex = $testIndex; $lowestSymbol = $symbol; }; }; return ($lowestIndex, $lowestSymbol); }; ############################################################################### # # Main Code # # The order in which functions are called here is critically important. Read the "Usage and Dependencies" sections of all the # packages before even thinking about rearranging these. # eval { # Check that our required packages are okay. NaturalDocs::File->CheckCompatibility(); # Almost everything requires Settings to be initialized. NaturalDocs::Settings->Load(); NaturalDocs::Project->LoadConfigFileInfo(); NaturalDocs::Topics->Load(); NaturalDocs::Languages->Load(); # Migrate from the old file names that were used prior to 1.14. NaturalDocs::Project->MigrateOldFiles(); if (!NaturalDocs::Settings->IsQuiet()) { print "Finding files and detecting changes...\n"; }; NaturalDocs::Project->LoadSourceFileInfo(); NaturalDocs::Project->LoadImageFileInfo(); # Register SourceDB extensions. Order is important. NaturalDocs::ImageReferenceTable->Register(); NaturalDocs::SymbolTable->Load(); NaturalDocs::ClassHierarchy->Load(); NaturalDocs::SourceDB->Load(); NaturalDocs::SymbolTable->Purge(); NaturalDocs::ClassHierarchy->Purge(); NaturalDocs::SourceDB->PurgeDeletedSourceFiles(); # Parse any supported files that have changed. my $filesToParse = NaturalDocs::Project->FilesToParse(); my $amount = scalar keys %$filesToParse; if ($amount > 0) { NaturalDocs::StatusMessage->Start('Parsing ' . $amount . ' file' . ($amount > 1 ? 's' : '') . '...', $amount); foreach my $file (keys %$filesToParse) { NaturalDocs::Parser->ParseForInformation($file); NaturalDocs::StatusMessage->CompletedItem(); }; }; # The symbol table is now fully resolved, so we can reduce its memory footprint. NaturalDocs::SymbolTable->PurgeResolvingInfo(); # Load and update the menu file. We need to do this after parsing so when it is updated, it will detect files where the # default menu title has changed and files that have added or deleted Natural Docs content. NaturalDocs::Menu->LoadAndUpdate(); # Build any files that need it. This needs to be run regardless of whether there are any files to build. It will handle its own # output messages. NaturalDocs::Builder->Run(); # Write the changes back to disk. NaturalDocs::Menu->Save(); NaturalDocs::Project->SaveImageFileInfo(); NaturalDocs::Project->SaveSourceFileInfo(); NaturalDocs::SymbolTable->Save(); NaturalDocs::ClassHierarchy->Save(); NaturalDocs::SourceDB->Save(); NaturalDocs::Settings->Save(); NaturalDocs::Topics->Save(); NaturalDocs::Languages->Save(); # Must be done last. NaturalDocs::Project->SaveConfigFileInfo(); if (!NaturalDocs::Settings->IsQuiet()) { print "Done.\n"; }; }; if ($EVAL_ERROR) # Oops. { NaturalDocs::Error->HandleDeath(); };