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