1#!/usr/local/bin/perl 2 3=begin nd 4 5 Script: NaturalDocs 6 ___________________________________________________________________________ 7 8 Version 1.52 9 10 Copyright � 2003-2010 Greg Valure 11 12 http://www.naturaldocs.org 13 14 Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL). Refer to the <License> for the 15 complete details. 16 17 18 Topic: Code Conventions 19 20 - Every package function is called with an arrow operator. It's needed for inheritance in some places, and consistency 21 when it's not. 22 23 - No constant will ever be zero or undef. Those are reserved so any piece of code can allow a "none of the above" option 24 and not worry about conflicts with an existing value. 25 26 - Existence hashes are hashes where the value doesn't matter. It acts more as a set, where the existence of the key is 27 the significant part. 28 29 30 Topic: File Format Conventions 31 32 - All integers appear in big-endian format. So a UInt16 should be handled with a 'n' in pack and unpack, not with a 'S'. 33 34 - UString16's are a big-endian UInt16 followed by that many UTF-8 bytes. A null-terminator is not stored. 35 36 - If a higher-level type is described in a file format, that means the loading and saving format is handled by that package. 37 For example, if you see <SymbolString> in the format, that means <NaturalDocs::SymbolString->ToBinaryFile()> and 38 <NaturalDocs::SymbolString->FromBinaryFile()> are used to manipulate it, and the underlying format should be treated 39 as opaque. 40 41=cut 42 43 44use strict; 45use integer; 46 47use 5.008; # When :encoding modifiers were allowed with file access. 48 49use English '-no_match_vars'; 50 51use FindBin; 52use lib "$FindBin::RealBin/Modules"; 53 54sub INIT 55 { 56 # 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 57 # function in some other file since that's the first piece of code to execute. 58 }; 59 60 61use NaturalDocs::Constants; 62use NaturalDocs::Version; 63use NaturalDocs::File; 64use NaturalDocs::Error; 65 66use NaturalDocs::LineReader; 67use NaturalDocs::ConfigFile; 68use NaturalDocs::BinaryFile; 69use NaturalDocs::StatusMessage; 70use NaturalDocs::SymbolString; 71use NaturalDocs::ReferenceString; 72use NaturalDocs::NDMarkup; 73 74use NaturalDocs::Settings; 75use NaturalDocs::Topics; 76use NaturalDocs::Languages; 77use NaturalDocs::Project; 78use NaturalDocs::Menu; 79use NaturalDocs::SymbolTable; 80use NaturalDocs::ClassHierarchy; 81use NaturalDocs::SourceDB; 82use NaturalDocs::ImageReferenceTable; 83use NaturalDocs::Parser; 84use NaturalDocs::Builder; 85 86 87 88############################################################################### 89# 90# Group: Basic Types 91# 92# Types used throughout the program. As Perl is a weakly-typed language unless you box things into objects, these types are 93# for documentation purposes and are not enforced. 94# 95# 96# Type: FileName 97# 98# A string representing the absolute, platform-dependent path to a file. Relative file paths are no longer in use anywhere in the 99# program. All path manipulation should be done through <NaturalDocs::File>. 100# 101# 102# Type: VersionInt 103# 104# A comparable integer representing a version number. Converting them to and from text and binary should be handled by 105# <NaturalDocs::Version>. 106# 107# 108# Type: SymbolString 109# 110# A scalar which encodes a normalized array of identifier strings representing a full or partially-resolved symbol. All symbols 111# must be retrieved from plain text via <NaturalDocs::SymbolString->FromText()> so that the separation and normalization is 112# always consistent. SymbolStrings are comparable via string compare functions and are sortable. 113# 114# 115# Type: ReferenceString 116# 117# All the information about a reference that makes it unique encoded into a string. This includes the <SymbolString> of the 118# reference, the scope <SymbolString> it appears in, the scope <SymbolStrings> it has access to via "using", and the 119# <ReferenceType>. This is done because if any of those parameters change, it needs to be treated as a completely separate 120# reference. 121# 122 123 124 125############################################################################### 126# Group: Support Functions 127# General functions that are used throughout the program, and that don't really fit anywhere else. 128 129 130# 131# Function: StringCompare 132# 133# Compares two strings so that the result is good for proper sorting. A proper sort orders the characters as 134# follows: 135# 136# - End of string. 137# - Whitespace. Line break-tab-space. 138# - Symbols, which is anything not included in the other entries. 139# - Numbers, 0-9. 140# - Letters, case insensitive except to break ties. 141# 142# If you use cmp instead of this function, the result would go by ASCII/Unicode values which would place certain symbols 143# between letters and numbers instead of having them all grouped together. Also, you would have to choose between case 144# sensitivity or complete case insensitivity, in which ties are broken arbitrarily. 145# 146# Returns: 147# 148# 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. 149# 150sub StringCompare #(a, b) 151 { 152 my ($a, $b) = @_; 153 154 if (!defined $a) 155 { 156 if (!defined $b) 157 { return 0; } 158 else 159 { return -1; }; 160 } 161 elsif (!defined $b) 162 { 163 return 1; 164 }; 165 166 my $translatedA = lc($a); 167 my $translatedB = lc($b); 168 169 $translatedA =~ tr/\n\r\t 0-9a-z/\x01\x02\x03\x04\xDB-\xFE/; 170 $translatedB =~ tr/\n\r\t 0-9a-z/\x01\x02\x03\x04\xDB-\xFE/; 171 172 my $result = $translatedA cmp $translatedB; 173 174 if ($result == 0) 175 { 176 # Break the tie by comparing their case. Lowercase before uppercase. 177 178 # If statement just to keep everything theoretically kosher, even though in practice we don't need this. 179 if (ord('A') > ord('a')) 180 { return ($a cmp $b); } 181 else 182 { return ($b cmp $a); }; 183 } 184 else 185 { return $result; }; 186 }; 187 188 189# 190# Function: ShortenToMatchStrings 191# 192# Compares two arrayrefs and shortens the first array to only contain shared entries. Assumes all entries are strings. 193# 194# Parameters: 195# 196# sharedArrayRef - The arrayref that will be shortened to only contain common elements. 197# compareArrayRef - The arrayref to match. 198# 199sub ShortenToMatchStrings #(sharedArrayRef, compareArrayRef) 200 { 201 my ($sharedArrayRef, $compareArrayRef) = @_; 202 203 my $index = 0; 204 205 while ($index < scalar @$sharedArrayRef && $index < scalar @$compareArrayRef && 206 $sharedArrayRef->[$index] eq $compareArrayRef->[$index]) 207 { $index++; }; 208 209 if ($index < scalar @$sharedArrayRef) 210 { splice(@$sharedArrayRef, $index); }; 211 }; 212 213 214# 215# Function: FindFirstSymbol 216# 217# Searches a string for a number of symbols to see which appears first. 218# 219# Parameters: 220# 221# string - The string to search. 222# symbols - An arrayref of symbols to look for. 223# index - The index to start at, if any. 224# 225# Returns: 226# 227# The array ( index, symbol ). 228# 229# index - The index the first symbol appears at, or -1 if none appear. 230# symbol - The symbol that appeared, or undef if none. 231# 232sub FindFirstSymbol #(string, symbols, index) 233 { 234 my ($string, $symbols, $index) = @_; 235 236 if (!defined $index) 237 { $index = 0; }; 238 239 my $lowestIndex = -1; 240 my $lowestSymbol; 241 242 foreach my $symbol (@$symbols) 243 { 244 my $testIndex = index($string, $symbol, $index); 245 246 if ($testIndex != -1 && ($lowestIndex == -1 || $testIndex < $lowestIndex)) 247 { 248 $lowestIndex = $testIndex; 249 $lowestSymbol = $symbol; 250 }; 251 }; 252 253 return ($lowestIndex, $lowestSymbol); 254 }; 255 256 257 258 259############################################################################### 260# 261# Main Code 262# 263# The order in which functions are called here is critically important. Read the "Usage and Dependencies" sections of all the 264# packages before even thinking about rearranging these. 265# 266 267 268eval { 269 270 # Check that our required packages are okay. 271 272 NaturalDocs::File->CheckCompatibility(); 273 274 275 # Almost everything requires Settings to be initialized. 276 277 NaturalDocs::Settings->Load(); 278 279 280 NaturalDocs::Project->LoadConfigFileInfo(); 281 282 NaturalDocs::Topics->Load(); 283 NaturalDocs::Languages->Load(); 284 285 286 # Migrate from the old file names that were used prior to 1.14. 287 288 NaturalDocs::Project->MigrateOldFiles(); 289 290 291 if (!NaturalDocs::Settings->IsQuiet()) 292 { print "Finding files and detecting changes...\n"; }; 293 294 NaturalDocs::Project->LoadSourceFileInfo(); 295 NaturalDocs::Project->LoadImageFileInfo(); 296 297 # Register SourceDB extensions. Order is important. 298 NaturalDocs::ImageReferenceTable->Register(); 299 300 NaturalDocs::SymbolTable->Load(); 301 NaturalDocs::ClassHierarchy->Load(); 302 NaturalDocs::SourceDB->Load(); 303 304 NaturalDocs::SymbolTable->Purge(); 305 NaturalDocs::ClassHierarchy->Purge(); 306 NaturalDocs::SourceDB->PurgeDeletedSourceFiles(); 307 308 309 # Parse any supported files that have changed. 310 311 my $filesToParse = NaturalDocs::Project->FilesToParse(); 312 my $amount = scalar keys %$filesToParse; 313 314 if ($amount > 0) 315 { 316 NaturalDocs::StatusMessage->Start('Parsing ' . $amount . ' file' . ($amount > 1 ? 's' : '') . '...', $amount); 317 318 foreach my $file (keys %$filesToParse) 319 { 320 NaturalDocs::Parser->ParseForInformation($file); 321 NaturalDocs::StatusMessage->CompletedItem(); 322 }; 323 }; 324 325 326 # The symbol table is now fully resolved, so we can reduce its memory footprint. 327 328 NaturalDocs::SymbolTable->PurgeResolvingInfo(); 329 330 331 # Load and update the menu file. We need to do this after parsing so when it is updated, it will detect files where the 332 # default menu title has changed and files that have added or deleted Natural Docs content. 333 334 NaturalDocs::Menu->LoadAndUpdate(); 335 336 337 # 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 338 # output messages. 339 340 NaturalDocs::Builder->Run(); 341 342 343 # Write the changes back to disk. 344 345 NaturalDocs::Menu->Save(); 346 NaturalDocs::Project->SaveImageFileInfo(); 347 NaturalDocs::Project->SaveSourceFileInfo(); 348 NaturalDocs::SymbolTable->Save(); 349 NaturalDocs::ClassHierarchy->Save(); 350 NaturalDocs::SourceDB->Save(); 351 NaturalDocs::Settings->Save(); 352 NaturalDocs::Topics->Save(); 353 NaturalDocs::Languages->Save(); 354 355 # Must be done last. 356 NaturalDocs::Project->SaveConfigFileInfo(); 357 358 if (!NaturalDocs::Settings->IsQuiet()) 359 { print "Done.\n"; }; 360 361}; 362 363if ($EVAL_ERROR) # Oops. 364 { 365 NaturalDocs::Error->HandleDeath(); 366 }; 367 368