OSDN Git Service

Version 5.91
[vbslib/main.git] / GPL_bin_fullset / NaturalDocs / Modules / NaturalDocs / Settings.pm
1 ###############################################################################
2 #
3 #   Package: NaturalDocs::Settings
4 #
5 ###############################################################################
6 #
7 #   A package to handle the command line and various other program settings.
8 #
9 ###############################################################################
10
11 # This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
12 # Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
13 # Refer to License.txt for the complete details
14
15 use Cwd ();
16
17 use NaturalDocs::Settings::BuildTarget;
18
19 use strict;
20 use integer;
21
22 package NaturalDocs::Settings;
23
24
25 ###############################################################################
26 # Group: Information
27
28 =pod begin nd
29
30     Topic: Usage and Dependencies
31
32         - The <Constant Functions> can be called immediately.
33
34         - Prior to initialization, <NaturalDocs::Builder> must have all its output packages registered.
35
36         - To initialize, call <Load()>.  All functions except <InputDirectoryNameOf()> will then be available.
37
38         - <GenerateDirectoryNames()> must be called before <InputDirectoryNameOf()> will work.  Currently it is called by
39           <NaturalDocs::Menu->LoadAndUpdate()>.
40
41
42     Architecture: Internal Overview
43
44         - <Load()> first parses the command line, gathering all the settings and checking for errors.  All <NaturalDocs::Builder>
45           packages must be registered before this is called because it needs their command line options.
46           <NaturalDocs::Project->ReparseEverything()> and <NaturalDocs::Project->RebuildEverything()> are called right away if -r
47           or -ro are used.
48
49         - Output directories are *not* named at this point.  See <Named Directories>.
50
51         - The previous settings from the last time Natural Docs was run are loaded and compared to the current settings.
52           <NaturalDocs::Project->ReparseEverything()> and <NaturalDocs::Project->RebuildEverything()> are called if there are
53           any differences that warrant it.
54
55         - It then waits for <GenerateDirectoryNames()> to be called by <NaturalDocs::Menu>.  The reason for this is that the
56           previous directory names are stored as hints in the menu file, for reasons explained in <Named Directories>.  Once that
57           happens all the unnamed directories have names generated for them so everything is named.  The package is completely
58           set up.
59
60         - The input directories are stored in an array instead of a hash because the order they were declared in matters.  If two
61           people use multiple input directories on separate computers without sharing a menu file, they should at least get consistent
62           directory names by declaring them in the same order.
63
64
65     Architecture: Named Directories
66
67         Ever since Natural Docs introduced multiple input directories in 1.16, they've had to be named.  Since they don't necessarily
68         extend from the same root anymore, they can't share an output directory without the risk of file name conflicts.  There was
69         an early attempt at giving them actual names, but now they're just numbered from 1.
70
71         Directory names aren't generated right away.  It waits for <Menu.txt> to load because that holds the obfuscated names from
72         the last run.  <NaturalDocs::Menu> then calls <GenerateDirectoryNames()> and passes those along as hints.
73         <GenerateDirectoryNames()> then applies them to any matches and generates new ones for any remaining.  This is done so
74         that output page locations can remain consistent when built on multiple computers, so long as the menu file is shared.  I tend
75         to think the menu file is the most likely configuration file to be shared.
76
77
78     Architecture: Removed Directories
79
80         Directories that were part of the previous run but aren't anymore are still stored in the package.  The primary reason, though
81         there may be others, is file purging.  If an input directory is removed, all the output files that were generated from anything
82         in it need to be removed.  To find out what the output file name was for a removed source file, it needs to be able to split it
83         from it's original input directory and know what that directory was named.  If this didn't happen those output files would be
84         orphaned, as was the case prior to 1.32.
85
86 =cut
87
88
89
90 ###############################################################################
91 # Group: Variables
92
93
94 # handle: PREVIOUS_SETTINGS_FILEHANDLE
95 # The file handle used with <PreviousSettings.nd>.
96
97 # array: inputDirectories
98 # An array of input directories.
99 my @inputDirectories;
100
101 # array: inputDirectoryNames
102 # An array of the input directory names.  Each name corresponds to the directory of the same index in <inputDirectories>.
103 my @inputDirectoryNames;
104
105 # array: imageDirectories
106 # An array of image directories.
107 my @imageDirectories;
108
109 # array: imageDirectoryNames
110 # An array of the image directory names.  Each name corresponds to the directory of the same index in <imageDirectories>.
111 my @imageDirectoryNames;
112
113 # array: relativeImageDirectories
114 # An array of the relative paths for images.  The asterisks found in the command line are not present.
115 my @relativeImageDirectories;
116
117 # array: excludedInputDirectories
118 # An array of input directories to exclude.
119 my @excludedInputDirectories;
120
121 # array: removedInputDirectories
122 # An array of input directories that were once in the command line but are no longer.
123 my @removedInputDirectories;
124
125 # array: removedInputDirectoryNames
126 # An array of the removed input directories' names.  Each name corresponds to the directory of the same index in
127 # <removedInputDirectories>.
128 my @removedInputDirectoryNames;
129
130 # array: removedImageDirectories
131 # An array of image directories that were once in the command line but are no longer.
132 my @removedImageDirectories;
133
134 # array: removedImageDirectoryNames
135 # An array of the removed image directories' names.  Each name corresponds to the directory of the same index in
136 # <removedImageDirectories>.
137 my @removedImageDirectoryNames;
138
139 # var: projectDirectory
140 # The project directory.
141 my $projectDirectory;
142
143 # array: buildTargets
144 # An array of <NaturalDocs::Settings::BuildTarget>s.
145 my @buildTargets;
146
147 # var: documentedOnly
148 # Whether undocumented code aspects should be included in the output.
149 my $documentedOnly;
150
151 # int: tabLength
152 # The number of spaces in tabs.
153 my $tabLength;
154
155 # bool: noAutoGroup
156 # Whether auto-grouping is turned off.
157 my $noAutoGroup;
158
159 # bool: onlyFileTitles
160 # Whether source files should always use the file name as the title.
161 my $onlyFileTitles;
162
163 # bool: isQuiet
164 # Whether the script should be run in quiet mode or not.
165 my $isQuiet;
166
167 # bool: rebuildData
168 # WHether most data files should be ignored and rebuilt.
169 my $rebuildData;
170
171 # array: styles
172 # An array of style names to use, most important first.
173 my @styles;
174
175 # var: highlightCode
176 # Whether syntax highlighting should be applied to code tags.
177 my $highlightCode;
178
179 # var: highlightAnonymous
180 # Whether syntax highlighting should be applied to anonymous code tags.
181 my $highlightAnonymous;
182
183
184 ###############################################################################
185 # Group: Files
186
187
188 #
189 #   File: PreviousSettings.nd
190 #
191 #   Stores the previous command line settings.
192 #
193 #   Format:
194 #
195 #       > [BINARY_FORMAT]
196 #       > [VersionInt: app version]
197 #
198 #       The file starts with the standard <BINARY_FORMAT> <VersionInt> header.
199 #
200 #       > [UInt8: tab length]
201 #       > [UInt8: documented only (0 or 1)]
202 #       > [UInt8: no auto-group (0 or 1)]
203 #       > [UInt8: only file titles (0 or 1)]
204 #               > [UInt8: highlight code (0 or 1)]
205 #               > [UInt8: highlight anonymous (0 or 1)]
206 #       >
207 #       > [UInt8: number of input directories]
208 #       > [UString16: input directory] [UString16: input directory name] ...
209 #
210 #       A count of input directories, then that number of directory/name pairs.
211 #
212 #       > [UInt8: number of output targets]
213 #       > [UString16: output directory] [UString16: output format command line option] ...
214 #
215 #       A count of output targets, then that number of directory/format pairs.
216 #
217 #
218 #   Revisions:
219 #
220 #               1.52:
221 #
222 #                       - Changed AString16s to UString16s.
223 #
224 #               1.51:
225 #
226 #                       - Removed charset.
227 #
228 #               1.5:
229 #
230 #                       - Added highlight code and highlight anonymous.
231 #
232 #       1.4:
233 #
234 #           - Added only file titles.
235 #
236 #       1.33:
237 #
238 #           - Added charset.
239 #
240 #       1.3:
241 #
242 #           - Removed headers-only, which was a 0/1 UInt8 after tab length.
243 #           - Change auto-group level (1 = no, 2 = yes, 3 = full only) to no auto-group (0 or 1).
244 #
245 #       1.22:
246 #
247 #           - Added auto-group level.
248 #
249 #       1.2:
250 #
251 #           - File was added to the project.  Prior to 1.2, it didn't exist.
252 #
253
254
255 ###############################################################################
256 # Group: Action Functions
257
258 #
259 #   Function: Load
260 #
261 #   Loads and parses all settings from the command line and configuration files.  Will exit if the options are invalid or the syntax
262 #   reference was requested.
263 #
264 sub Load
265     {
266     my ($self) = @_;
267
268     $self->ParseCommandLine();
269     $self->LoadAndComparePreviousSettings();
270     };
271
272
273 #
274 #   Function: Save
275 #
276 #   Saves all settings in configuration files to disk.
277 #
278 sub Save
279     {
280     my ($self) = @_;
281
282     $self->SavePreviousSettings();
283     };
284
285
286 #
287 #   Function: GenerateDirectoryNames
288 #
289 #   Generates names for each of the input and image directories, which can later be retrieved with <InputDirectoryNameOf()>
290 #   and <ImageDirectoryNameOf()>.
291 #
292 #   Parameters:
293 #
294 #       inputHints - A hashref of suggested input directory names, where the keys are the directories and the values are the names.
295 #                        These take precedence over anything generated.  You should include names for directories that are no longer in
296 #                        the command line.  This parameter may be undef.
297 #       imageHints - Same as inputHints, only for the image directories.
298 #
299 sub GenerateDirectoryNames #(hashref inputHints, hashref imageHints)
300     {
301     my ($self, $inputHints, $imageHints) = @_;
302
303     my %usedInputNames;
304     my %usedImageNames;
305
306
307     if (defined $inputHints)
308         {
309         # First, we have to convert all non-numeric names to numbers, since they may come from a pre-1.32 menu file.  We do it
310         # here instead of in NaturalDocs::Menu to keep the naming scheme centralized.
311
312         my @names = values %$inputHints;
313         my $hasNonNumeric;
314
315         foreach my $name (@names)
316             {
317             if ($name !~ /^[0-9]+$/)
318                 {
319                 $hasNonNumeric = 1;
320                 last;
321                 };
322             };
323
324
325         if ($hasNonNumeric)
326             {
327             # Hash mapping old names to new names.
328             my %conversion;
329
330             # The sequential number to use.  Starts at two because we want 'default' to be one.
331             my $currentNumber = 2;
332
333             # If there's only one name, we set it to one no matter what it was set to before.
334             if (scalar @names == 1)
335                 {  $conversion{$names[0]} = 1;  }
336             else
337                 {
338                 # We sort the list first because we want the end result to be predictable.  This conversion could be happening on many
339                 # machines, and they may not all specify the input directories in the same order.  They need to all come up with the
340                 # same result.
341                 @names = sort @names;
342
343                 foreach my $name (@names)
344                     {
345                     if ($name eq 'default')
346                         {  $conversion{$name} = 1;  }
347                     else
348                         {
349                         $conversion{$name} = $currentNumber;
350                         $currentNumber++;
351                         };
352                     };
353                 };
354
355             # Convert them to the new names.
356             foreach my $directory (keys %$inputHints)
357                 {
358                 $inputHints->{$directory} = $conversion{ $inputHints->{$directory} };
359                 };
360             };
361
362
363         # Now we apply all the names from the hints, and save any unused ones as removed directories.
364
365         for (my $i = 0; $i < scalar @inputDirectories; $i++)
366             {
367             if (exists $inputHints->{$inputDirectories[$i]})
368                 {
369                 $inputDirectoryNames[$i] = $inputHints->{$inputDirectories[$i]};
370                 $usedInputNames{ $inputDirectoryNames[$i] } = 1;
371                 delete $inputHints->{$inputDirectories[$i]};
372                 };
373             };
374
375
376         # Any remaining hints are saved as removed directories.
377
378         while (my ($directory, $name) = each %$inputHints)
379             {
380             push @removedInputDirectories, $directory;
381             push @removedInputDirectoryNames, $name;
382             };
383         };
384
385
386     if (defined $imageHints)
387         {
388         # Image directory names were never non-numeric, so there is no conversion.  Apply all the names from the hints.
389
390         for (my $i = 0; $i < scalar @imageDirectories; $i++)
391             {
392             if (exists $imageHints->{$imageDirectories[$i]})
393                 {
394                 $imageDirectoryNames[$i] = $imageHints->{$imageDirectories[$i]};
395                 $usedImageNames{ $imageDirectoryNames[$i] } = 1;
396                 delete $imageHints->{$imageDirectories[$i]};
397                 };
398             };
399
400
401         # Any remaining hints are saved as removed directories.
402
403         while (my ($directory, $name) = each %$imageHints)
404             {
405             push @removedImageDirectories, $directory;
406             push @removedImageDirectoryNames, $name;
407             };
408         };
409
410
411     # Now we generate names for anything remaining.
412
413     my $inputCounter = 1;
414
415     for (my $i = 0; $i < scalar @inputDirectories; $i++)
416         {
417         if (!defined $inputDirectoryNames[$i])
418             {
419             while (exists $usedInputNames{$inputCounter})
420                 {  $inputCounter++;  };
421
422             $inputDirectoryNames[$i] = $inputCounter;
423             $usedInputNames{$inputCounter} = 1;
424
425             $inputCounter++;
426             };
427         };
428
429
430     my $imageCounter = 1;
431
432     for (my $i = 0; $i < scalar @imageDirectories; $i++)
433         {
434         if (!defined $imageDirectoryNames[$i])
435             {
436             while (exists $usedImageNames{$imageCounter})
437                 {  $imageCounter++;  };
438
439             $imageDirectoryNames[$i] = $imageCounter;
440             $usedImageNames{$imageCounter} = 1;
441
442             $imageCounter++;
443             };
444         };
445     };
446
447
448
449 ###############################################################################
450 # Group: Information Functions
451
452
453 #
454 #   Function: InputDirectories
455 #
456 #   Returns an arrayref of input directories.  Do not change.
457 #
458 #   This will not return any removed input directories.
459 #
460 sub InputDirectories
461     {  return \@inputDirectories;  };
462
463 #
464 #   Function: InputDirectoryNameOf
465 #
466 #   Returns the generated name of the passed input directory.  <GenerateDirectoryNames()> must be called once before this
467 #   function is available.
468 #
469 #   If a name for a removed input directory is available, it will be returned as well.
470 #
471 sub InputDirectoryNameOf #(directory)
472     {
473     my ($self, $directory) = @_;
474
475     for (my $i = 0; $i < scalar @inputDirectories; $i++)
476         {
477         if ($directory eq $inputDirectories[$i])
478             {  return $inputDirectoryNames[$i];  };
479         };
480
481     for (my $i = 0; $i < scalar @removedInputDirectories; $i++)
482         {
483         if ($directory eq $removedInputDirectories[$i])
484             {  return $removedInputDirectoryNames[$i];  };
485         };
486
487     return undef;
488     };
489
490
491 #
492 #   Function: SplitFromInputDirectory
493 #
494 #   Takes an input file name and returns the array ( inputDirectory, relativePath ).
495 #
496 #   If the file cannot be split from an input directory, it will try to do it with the removed input directories.
497 #
498 sub SplitFromInputDirectory #(file)
499     {
500     my ($self, $file) = @_;
501
502     foreach my $directory (@inputDirectories, @removedInputDirectories)
503         {
504         if (NaturalDocs::File->IsSubPathOf($directory, $file))
505             {  return ( $directory, NaturalDocs::File->MakeRelativePath($directory, $file) );  };
506         };
507
508     return ( );
509     };
510
511
512 #
513 #   Function: ImageDirectories
514 #
515 #   Returns an arrayref of image directories.  Do not change.
516 #
517 #   This will not return any removed image directories.
518 #
519 sub ImageDirectories
520     {  return \@imageDirectories;  };
521
522
523 #
524 #   Function: ImageDirectoryNameOf
525 #
526 #   Returns the generated name of the passed image or input directory.  <GenerateDirectoryNames()> must be called once before
527 #   this function is available.
528 #
529 #   If a name for a removed input or image directory is available, it will be returned as well.
530 #
531 sub ImageDirectoryNameOf #(directory)
532     {
533     my ($self, $directory) = @_;
534
535     for (my $i = 0; $i < scalar @imageDirectories; $i++)
536         {
537         if ($directory eq $imageDirectories[$i])
538             {  return $imageDirectoryNames[$i];  };
539         };
540
541     for (my $i = 0; $i < scalar @removedImageDirectories; $i++)
542         {
543         if ($directory eq $removedImageDirectories[$i])
544             {  return $removedImageDirectoryNames[$i];  };
545         };
546
547     return undef;
548     };
549
550
551 #
552 #   Function: SplitFromImageDirectory
553 #
554 #   Takes an input image file name and returns the array ( imageDirectory, relativePath ).
555 #
556 #   If the file cannot be split from an image directory, it will try to do it with the removed image directories.
557 #
558 sub SplitFromImageDirectory #(file)
559     {
560     my ($self, $file) = @_;
561
562     foreach my $directory (@imageDirectories, @removedImageDirectories)
563         {
564         if (NaturalDocs::File->IsSubPathOf($directory, $file))
565             {  return ( $directory, NaturalDocs::File->MakeRelativePath($directory, $file) );  };
566         };
567
568     return ( );
569     };
570
571
572 #
573 #   Function: RelativeImageDirectories
574 #
575 #   Returns an arrayref of relative image directories.  Do not change.
576 #
577 sub RelativeImageDirectories
578     {  return \@relativeImageDirectories;  };
579
580
581 # Function: ExcludedInputDirectories
582 # Returns an arrayref of input directories to exclude.  Do not change.
583 sub ExcludedInputDirectories
584     {  return \@excludedInputDirectories;  };
585
586
587 # Function: BuildTargets
588 # Returns an arrayref of <NaturalDocs::Settings::BuildTarget>s.  Do not change.
589 sub BuildTargets
590     {  return \@buildTargets;  };
591
592
593 #
594 #   Function: OutputDirectoryOf
595 #
596 #   Returns the output directory of a builder object.
597 #
598 #   Parameters:
599 #
600 #       object - The builder object, whose class is derived from <NaturalDocs::Builder::Base>.
601 #
602 #   Returns:
603 #
604 #       The builder directory, or undef if the object wasn't found..
605 #
606 sub OutputDirectoryOf #(object)
607     {
608     my ($self, $object) = @_;
609
610     foreach my $buildTarget (@buildTargets)
611         {
612         if ($buildTarget->Builder() == $object)
613             {  return $buildTarget->Directory();  };
614         };
615
616     return undef;
617     };
618
619
620 # Function: Styles
621 # Returns an arrayref of the styles associated with the output.
622 sub Styles
623     {  return \@styles;  };
624
625 # Function: ProjectDirectory
626 # Returns the project directory.
627 sub ProjectDirectory
628     {  return $projectDirectory;  };
629
630 # Function: ProjectDataDirectory
631 # Returns the project data directory.
632 sub ProjectDataDirectory
633     {  return NaturalDocs::File->JoinPaths($projectDirectory, 'Data', 1);  };
634
635 # Function: StyleDirectory
636 # Returns the main style directory.
637 sub StyleDirectory
638     {  return NaturalDocs::File->JoinPaths($FindBin::RealBin, 'Styles', 1);  };
639
640 # Function: JavaScriptDirectory
641 # Returns the main JavaScript directory.
642 sub JavaScriptDirectory
643     {  return NaturalDocs::File->JoinPaths($FindBin::RealBin, 'JavaScript', 1);  };
644
645 # Function: ConfigDirectory
646 # Returns the main configuration directory.
647 sub ConfigDirectory
648     {  return NaturalDocs::File->JoinPaths($FindBin::RealBin, 'Config', 1);  };
649
650 # Function: DocumentedOnly
651 # Returns whether undocumented code aspects should be included in the output.
652 sub DocumentedOnly
653     {  return $documentedOnly;  };
654
655 # Function: TabLength
656 # Returns the number of spaces tabs should be expanded to.
657 sub TabLength
658     {  return $tabLength;  };
659
660 # Function: NoAutoGroup
661 # Returns whether auto-grouping is turned off.
662 sub NoAutoGroup
663     {  return $noAutoGroup;  };
664
665 # Function: OnlyFileTitles
666 # Returns whether source files should always use the file name as the title.
667 sub OnlyFileTitles
668     {  return $onlyFileTitles;  };
669
670 # Function: IsQuiet
671 # Returns whether the script should be run in quiet mode or not.
672 sub IsQuiet
673     {  return $isQuiet;  };
674
675 # Function: RebuildData
676 # Returns whether all data files should be ignored and rebuilt.
677 sub RebuildData
678     {  return $rebuildData;  };
679
680 # Function: HighlightCode
681 # Returns whether to apply syntax highlighting (start code) sections.
682 sub HighlightCode
683         {  return $highlightCode;  }
684
685 # Function: HighlightAnonymous
686 # Returns whether to apply syntax highlighting to anonymous code sections designated with :, >, or |.
687 sub HighlightAnonymous
688         {  return $highlightAnonymous;  }
689
690
691 ###############################################################################
692 # Group: Constant Functions
693
694 #
695 #   Function: AppVersion
696 #
697 #   Returns Natural Docs' version number as an integer.  Use <TextAppVersion()> to get a printable version.
698 #
699 sub AppVersion
700     {
701     my ($self) = @_;
702     return NaturalDocs::Version->FromString($self->TextAppVersion());
703     };
704
705 #
706 #   Function: TextAppVersion
707 #
708 #   Returns Natural Docs' version number as plain text.
709 #
710 sub TextAppVersion
711     {
712     return '1.52';
713     };
714
715 #
716 #   Function: AppURL
717 #
718 #   Returns a string of the project's current web address.
719 #
720 sub AppURL
721     {  return 'http://www.naturaldocs.org';  };
722
723
724
725 ###############################################################################
726 # Group: Support Functions
727
728
729 #
730 #   Function: ParseCommandLine
731 #
732 #   Parses and validates the command line.  Will cause the script to exit if the options ask for the syntax reference or
733 #   are invalid.
734 #
735 sub ParseCommandLine
736     {
737     my ($self) = @_;
738
739     my %synonyms = ( 'input'    => '-i',
740                                   'source' => '-i',
741                                   'excludeinput' => '-xi',
742                                   'excludesource' => '-xi',
743                                   'images' => '-img',
744                                   'output'  => '-o',
745                                   'project' => '-p',
746                                   'documentedonly' => '-do',
747                                   'style'    => '-s',
748                                   'rebuild' => '-r',
749                                   'rebuildoutput' => '-ro',
750                                   'tablength' => '-t',
751                                   'quiet'    => '-q',
752                                   'headersonly' => '-ho',
753                                   'help'     => '-h',
754                                   'autogroup' => '-ag',
755                                   'noautogroup' => '-nag',
756                                   'onlyfiletitles' => '-oft',
757                                   'onlyfiletitle' => '-oft',
758                                   'highlight' => '-hl',
759                                   'highlighting' => '-hl' );
760
761
762     my @errorMessages;
763
764     my $valueRef;
765     my $option;
766
767     my @outputStrings;
768     my @imageStrings;
769     my $highlightString;
770
771
772     # Sometimes $valueRef is set to $ignored instead of undef because we don't want certain errors to cause other,
773     # unnecessary errors.  For example, if they set the input directory twice, we want to show that error and swallow the
774     # specified directory without complaint.  Otherwise it would complain about the directory too as if it were random crap
775     # inserted into the command line.
776     my $ignored;
777
778     my $index = 0;
779
780     while ($index < scalar @ARGV)
781         {
782         my $arg = $ARGV[$index];
783
784         if (substr($arg, 0, 1) eq '-')
785             {
786             $option = lc($arg);
787
788             # Support options like -t2 as well as -t 2.
789             if ($option =~ /^([^0-9]+)([0-9]+)$/)
790                 {
791                 $option = $1;
792                 splice(@ARGV, $index + 1, 0, $2);
793                 };
794
795             # Convert long forms to short.
796             if (substr($option, 1, 1) eq '-')
797                 {
798                 # Strip all dashes.
799                 my $newOption = $option;
800                 $newOption =~ tr/-//d;
801
802                 if (exists $synonyms{$newOption})
803                     {  $option = $synonyms{$newOption};  }
804                 }
805
806             if ($option eq '-i')
807                 {
808                 push @inputDirectories, undef;
809                 $valueRef = \$inputDirectories[-1];
810                 }
811             elsif ($option eq '-xi')
812                 {
813                 push @excludedInputDirectories, undef;
814                 $valueRef = \$excludedInputDirectories[-1];
815                 }
816             elsif ($option eq '-img')
817                 {
818                 push @imageStrings, undef;
819                 $valueRef = \$imageStrings[-1];
820                 }
821             elsif ($option eq '-p')
822                 {
823                 if (defined $projectDirectory)
824                     {
825                     push @errorMessages, 'You cannot have more than one project directory.';
826                     $valueRef = \$ignored;
827                     }
828                 else
829                     {  $valueRef = \$projectDirectory;  };
830                 }
831             elsif ($option eq '-o')
832                 {
833                 push @outputStrings, undef;
834                 $valueRef = \$outputStrings[-1];
835                 }
836             elsif ($option eq '-s')
837                 {
838                 $valueRef = \$styles[0];
839                 }
840             elsif ($option eq '-t')
841                 {
842                 $valueRef = \$tabLength;
843                 }
844             elsif ($option eq '-hl')
845                 {
846                 $valueRef = \$highlightString;
847                 }
848             elsif ($option eq '-ag')
849                 {
850                 push @errorMessages, 'The -ag setting is no longer supported.  You can use -nag (--no-auto-group) to turn off '
851                                                . "auto-grouping, but there aren't multiple levels anymore.";
852                 $valueRef = \$ignored;
853                 }
854
855             # Options that aren't followed by content.
856             else
857                 {
858                 $valueRef = undef;
859
860                 if ($option eq '-r')
861                     {
862                     NaturalDocs::Project->ReparseEverything();
863                     NaturalDocs::Project->RebuildEverything();
864                     $rebuildData = 1;
865                     }
866                 elsif ($option eq '-ro')
867                     {
868                     NaturalDocs::Project->RebuildEverything();
869                     }
870                 elsif ($option eq '-do')
871                     {  $documentedOnly = 1;  }
872                 elsif ($option eq '-oft')
873                     {  $onlyFileTitles = 1;  }
874                 elsif ($option eq '-q')
875                     {  $isQuiet = 1;  }
876                 elsif ($option eq '-ho')
877                     {
878                     push @errorMessages, 'The -ho setting is no longer supported.  You can have Natural Docs skip over the source file '
879                                                    . 'extensions by editing Languages.txt in your project directory.';
880                     }
881                 elsif ($option eq '-nag')
882                     {  $noAutoGroup = 1;  }
883                 elsif ($option eq '-?' || $option eq '-h')
884                     {
885                     $self->PrintSyntax();
886                     exit;
887                     }
888                 else
889                     {  push @errorMessages, 'Unrecognized option ' . $option;  };
890
891                 };
892
893             }
894
895         # Is a segment of text, not an option...
896         else
897             {
898             if (defined $valueRef)
899                 {
900                 # We want to preserve spaces in paths.
901                 if (defined $$valueRef)
902                     {  $$valueRef .= ' ';  };
903
904                 $$valueRef .= $arg;
905                 }
906
907             else
908                 {
909                 push @errorMessages, 'Unrecognized element ' . $arg;
910                 };
911             };
912
913         $index++;
914         };
915
916
917     # Validate the style, if specified.
918
919     if ($styles[0])
920         {
921         my @stylePieces = split(/ +/, $styles[0]);
922         @styles = ( );
923
924         while (scalar @stylePieces)
925             {
926             if (lc($stylePieces[0]) eq 'custom')
927                 {
928                 push @errorMessages, 'The "Custom" style setting is no longer supported.  Copy your custom style sheet to your '
929                                                . 'project directory and you can refer to it with -s.';
930                 shift @stylePieces;
931                 }
932             else
933                 {
934                 # People may use styles with spaces in them.  If a style doesn't exist, we need to join the pieces until we find one that
935                 # does or we run out of pieces.
936
937                 my $extras = 0;
938                 my $success;
939
940                 while ($extras < scalar @stylePieces)
941                     {
942                     my $style;
943
944                     if (!$extras)
945                         {  $style = $stylePieces[0];  }
946                     else
947                         {  $style = join(' ', @stylePieces[0..$extras]);  };
948
949                     my $cssFile = NaturalDocs::File->JoinPaths( $self->StyleDirectory(), $style . '.css' );
950                     if (-e $cssFile)
951                         {
952                         push @styles, $style;
953                         splice(@stylePieces, 0, 1 + $extras);
954                         $success = 1;
955                         last;
956                         }
957                     else
958                         {
959                         $cssFile = NaturalDocs::File->JoinPaths( $self->ProjectDirectory(), $style . '.css' );
960
961                         if (-e $cssFile)
962                             {
963                             push @styles, $style;
964                             splice(@stylePieces, 0, 1 + $extras);
965                             $success = 1;
966                             last;
967                             }
968                         else
969                             {  $extras++;  };
970                         };
971                     };
972
973                 if (!$success)
974                     {
975                     push @errorMessages, 'The style "' . $stylePieces[0] . '" does not exist.';
976                     shift @stylePieces;
977                     };
978                 };
979             };
980         }
981     else
982         {  @styles = ( 'Default' );  };
983
984
985     # Decode and validate the output strings.
986
987     my %outputDirectories;
988
989     foreach my $outputString (@outputStrings)
990         {
991         my ($format, $directory) = split(/ /, $outputString, 2);
992
993         if (!defined $directory)
994             {  push @errorMessages, 'The -o option needs two parameters: -o [format] [directory]';  }
995         else
996             {
997             if (!NaturalDocs::File->PathIsAbsolute($directory))
998                 {  $directory = NaturalDocs::File->JoinPaths(Cwd::cwd(), $directory, 1);  };
999
1000             $directory = NaturalDocs::File->CanonizePath($directory);
1001
1002             if (! -e $directory || ! -d $directory)
1003                 {
1004                 # They may have forgotten the format portion and the directory name had a space in it.
1005                 if (-e ($format . ' ' . $directory) && -d ($format . ' ' . $directory))
1006                     {
1007                     push @errorMessages, 'The -o option needs two parameters: -o [format] [directory]';
1008                     $format = undef;
1009                     }
1010                 else
1011                     {  push @errorMessages, 'The output directory ' . $directory . ' does not exist.';  }
1012                 }
1013             elsif (exists $outputDirectories{$directory})
1014                 {  push @errorMessages, 'You cannot specify the output directory ' . $directory . ' more than once.';  }
1015             else
1016                 {  $outputDirectories{$directory} = 1;  };
1017
1018             if (defined $format)
1019                 {
1020                 my $builderPackage = NaturalDocs::Builder->OutputPackageOf($format);
1021
1022                 if (defined $builderPackage)
1023                     {
1024                     push @buildTargets,
1025                             NaturalDocs::Settings::BuildTarget->New($builderPackage->New(), $directory);
1026                     }
1027                 else
1028                     {
1029                     push @errorMessages, 'The output format ' . $format . ' doesn\'t exist or is not installed.';
1030                     $valueRef = \$ignored;
1031                     };
1032                 };
1033             };
1034         };
1035
1036     if (!scalar @buildTargets)
1037         {  push @errorMessages, 'You did not specify an output directory.';  };
1038
1039
1040     # Decode and validate the image strings.
1041
1042     foreach my $imageString (@imageStrings)
1043         {
1044         if ($imageString =~ /^ *\*/)
1045             {
1046             # The below NaturalDocs::File functions assume everything is canonized.
1047             $imageString = NaturalDocs::File->CanonizePath($imageString);
1048
1049             my ($volume, $directoryString) = NaturalDocs::File->SplitPath($imageString, 1);
1050             my @directories = NaturalDocs::File->SplitDirectories($directoryString);
1051
1052             shift @directories;
1053
1054             $directoryString = NaturalDocs::File->JoinDirectories(@directories);
1055             push @relativeImageDirectories, NaturalDocs::File->JoinPath($volume, $directoryString);
1056             }
1057         else
1058             {
1059             if (!NaturalDocs::File->PathIsAbsolute($imageString))
1060                 {  $imageString = NaturalDocs::File->JoinPaths(Cwd::cwd(), $imageString, 1);  };
1061
1062             $imageString = NaturalDocs::File->CanonizePath($imageString);
1063
1064             if (! -e $imageString || ! -d $imageString)
1065                 {  push @errorMessages, 'The image directory ' . $imageString . ' does not exist.';  };
1066
1067             push @imageDirectories, $imageString;
1068             };
1069         };
1070
1071
1072     # Make sure the input and project directories are specified, canonized, and exist.
1073
1074     if (scalar @inputDirectories)
1075         {
1076         for (my $i = 0; $i < scalar @inputDirectories; $i++)
1077             {
1078             if (!NaturalDocs::File->PathIsAbsolute($inputDirectories[$i]))
1079                 {  $inputDirectories[$i] = NaturalDocs::File->JoinPaths(Cwd::cwd(), $inputDirectories[$i], 1);  };
1080
1081             $inputDirectories[$i] = NaturalDocs::File->CanonizePath($inputDirectories[$i]);
1082
1083             if (! -e $inputDirectories[$i] || ! -d $inputDirectories[$i])
1084                 {  push @errorMessages, 'The input directory ' . $inputDirectories[$i] . ' does not exist.';  };
1085             };
1086         }
1087     else
1088         {  push @errorMessages, 'You did not specify an input (source) directory.';  };
1089
1090     if (defined $projectDirectory)
1091         {
1092         if (!NaturalDocs::File->PathIsAbsolute($projectDirectory))
1093             {  $projectDirectory = NaturalDocs::File->JoinPaths(Cwd::cwd(), $projectDirectory, 1);  };
1094
1095         $projectDirectory = NaturalDocs::File->CanonizePath($projectDirectory);
1096
1097         if (! -e $projectDirectory || ! -d $projectDirectory)
1098             {  push @errorMessages, 'The project directory ' . $projectDirectory . ' does not exist.';  };
1099
1100         # Create the Data subdirectory if it doesn't exist.
1101         NaturalDocs::File->CreatePath( NaturalDocs::File->JoinPaths($projectDirectory, 'Data', 1) );
1102         }
1103     else
1104         {  push @errorMessages, 'You did not specify a project directory.';  };
1105
1106
1107     # Make sure the excluded input directories are canonized, and add the project and output directories to the list.
1108
1109     for (my $i = 0; $i < scalar @excludedInputDirectories; $i++)
1110         {
1111         if (!NaturalDocs::File->PathIsAbsolute($excludedInputDirectories[$i]))
1112             {  $excludedInputDirectories[$i] = NaturalDocs::File->JoinPaths(Cwd::cwd(), $excludedInputDirectories[$i], 1);  };
1113
1114         $excludedInputDirectories[$i] = NaturalDocs::File->CanonizePath($excludedInputDirectories[$i]);
1115         };
1116
1117     push @excludedInputDirectories, $projectDirectory;
1118
1119     foreach my $buildTarget (@buildTargets)
1120         {
1121         push @excludedInputDirectories, $buildTarget->Directory();
1122         };
1123
1124
1125     # Determine the tab length, and default to four if not specified.
1126
1127     if (defined $tabLength)
1128         {
1129         if ($tabLength !~ /^[0-9]+$/)
1130             {  push @errorMessages, 'The tab length must be a number.';  };
1131         }
1132     else
1133         {  $tabLength = 4;  };
1134
1135
1136     # Decode and validate the highlight setting.
1137
1138     if (defined $highlightString)
1139         {
1140         $highlightString = lc($highlightString);
1141
1142         if ($highlightString eq 'off')
1143                 {
1144                 $highlightCode = undef;
1145                 $highlightAnonymous = undef;
1146                 }
1147         elsif ($highlightString eq 'code')
1148                 {
1149                 $highlightCode = 1;
1150                 $highlightAnonymous = undef;
1151                 }
1152         elsif ($highlightString eq 'all')
1153                 {
1154                 $highlightCode = 1;
1155                 $highlightAnonymous = 1;
1156                 }
1157         else
1158                 {  push @errorMessages, $highlightString . ' is not a valid value for --highlight.';  }
1159         }
1160     else
1161         {
1162         $highlightCode = 1;
1163         $highlightAnonymous = undef;
1164         }
1165
1166
1167     # Exit with the error message if there was one.
1168
1169     if (scalar @errorMessages)
1170         {
1171         print join("\n", @errorMessages) . "\nType NaturalDocs -h to see the syntax reference.\n";
1172         exit;
1173         };
1174     };
1175
1176 #
1177 #   Function: PrintSyntax
1178 #
1179 #   Prints the syntax reference.
1180 #
1181 sub PrintSyntax
1182     {
1183     my ($self) = @_;
1184
1185     # Make sure all line lengths are under 80 characters.
1186
1187     print
1188
1189     "Natural Docs, version " . $self->TextAppVersion() . "\n"
1190     . $self->AppURL() . "\n"
1191     . "This program is licensed under version 3 of the AGPL\n"
1192     . "Refer to License.txt for the complete details\n"
1193     . "--------------------------------------\n"
1194     . "\n"
1195     . "Syntax:\n"
1196     . "\n"
1197     . "    NaturalDocs -i [input (source) directory]\n"
1198     . "               (-i [input (source) directory] ...)\n"
1199     . "                -o [output format] [output directory]\n"
1200     . "               (-o [output format] [output directory] ...)\n"
1201     . "                -p [project directory]\n"
1202     . "                [options]\n"
1203     . "\n"
1204     . "Examples:\n"
1205     . "\n"
1206     . "    NaturalDocs -i C:\\My Project\\Source -o HTML C:\\My Project\\Docs\n"
1207     . "                -p C:\\My Project\\Natural Docs\n"
1208     . "    NaturalDocs -i /src/project -o HTML /doc/project\n"
1209     . "                -p /etc/naturaldocs/project -s Small -q\n"
1210     . "\n"
1211     . "Required Parameters:\n"
1212     . "\n"
1213     . " -i [dir]\n--input [dir]\n--source [dir]\n"
1214     . "     Specifies an input (source) directory.  Required.\n"
1215     . "     Can be specified multiple times.\n"
1216     . "\n"
1217     . " -o [fmt] [dir]\n--output [fmt] [dir]\n"
1218     . "    Specifies an output format and directory.  Required.\n"
1219     . "    Can be specified multiple times, but only once per directory.\n"
1220     . "    Possible output formats:\n";
1221
1222     $self->PrintOutputFormats('    - ');
1223
1224     print
1225     "\n"
1226     . " -p [dir]\n--project [dir]\n"
1227     . "    Specifies the project directory.  Required.\n"
1228     . "    There needs to be a unique project directory for every source directory.\n"
1229     . "\n"
1230     . "Optional Parameters:\n"
1231     . "\n"
1232     . " -s [style] ([style] [style] ...)\n--style [style] ([style] [style] ...)\n"
1233     . "    Specifies the CSS style when building HTML output.  If multiple styles are\n"
1234     . "    specified, they will all be included in the order given.\n"
1235     . "\n"
1236     . " -img [image directory]\n--image [image directory]\n"
1237     . "    Specifies an image directory.  Can be specified multiple times.\n"
1238     . "    Start with * to specify a relative directory, as in -img */images.\n"
1239     . "\n"
1240     . " -do\n--documented-only\n"
1241     . "    Specifies only documented code aspects should be included in the output.\n"
1242     . "\n"
1243     . " -t [len]\n--tab-length [len]\n"
1244     . "    Specifies the number of spaces tabs should be expanded to.  This only needs\n"
1245     . "    to be set if you use tabs in example code and text diagrams.  Defaults to 4.\n"
1246     . "\n"
1247     . " -xi [dir]\n--exclude-input [dir]\n--exclude-source [dir]\n"
1248     . "    Excludes an input (source) directory from the documentation.\n"
1249     . "    Automatically done for the project and output directories.  Can\n"
1250     . "    be specified multiple times.\n"
1251     . "\n"
1252     . " -nag\n--no-auto-group\n"
1253     . "    Turns off auto-grouping completely.\n"
1254     . "\n"
1255     . " -oft\n--only-file-titles\n"
1256     . "    Source files will only use the file name as the title.\n"
1257     . "\n"
1258     . " -hl [option]\n--highlight [option]\n"
1259     . "    Specifies when syntax highlighting should be applied.  Defaults to code.\n"
1260     . "    off  - No syntax highlighting is applied.\n"
1261     . "    code - Syntax highlighting is only applied to prototypes and (start code)\n"
1262     . "           segments.\n"
1263     . "    all  - Systax highlighting is applied to prototypes, (start code) segments,\n"
1264     . "           and lines starting with >, :, or |."
1265     . "\n"
1266     . " -r\n--rebuild\n"
1267     . "    Rebuilds all output and data files from scratch.\n"
1268     . "    Does not affect the menu file.\n"
1269     . "\n"
1270     . " -ro\n--rebuild-output\n"
1271     . "    Rebuilds all output files from scratch.\n"
1272     . "\n"
1273     . " -q\n--quiet\n"
1274     . "    Suppresses all non-error output.\n"
1275     . "\n"
1276     . " -?\n -h\n--help\n"
1277     . "    Displays this syntax reference.\n";
1278     };
1279
1280
1281 #
1282 #   Function: PrintOutputFormats
1283 #
1284 #   Prints all the possible output formats that can be specified with -o.  Each one will be placed on its own line.
1285 #
1286 #   Parameters:
1287 #
1288 #       prefix - Characters to prefix each one with, such as for indentation.
1289 #
1290 sub PrintOutputFormats #(prefix)
1291     {
1292     my ($self, $prefix) = @_;
1293
1294     my $outputPackages = NaturalDocs::Builder::OutputPackages();
1295
1296     foreach my $outputPackage (@$outputPackages)
1297         {
1298         print $prefix . $outputPackage->CommandLineOption() . "\n";
1299         };
1300     };
1301
1302
1303 #
1304 #   Function: LoadAndComparePreviousSettings
1305 #
1306 #   Loads <PreviousSettings.nd> and compares the values there with those in the command line.  If differences require it,
1307 #   sets <rebuildData> and/or <rebuildOutput>.
1308 #
1309 sub LoadAndComparePreviousSettings
1310     {
1311     my ($self) = @_;
1312
1313     my $fileIsOkay;
1314
1315     if (!NaturalDocs::Settings->RebuildData())
1316         {
1317         my $version;
1318
1319         if (NaturalDocs::BinaryFile->OpenForReading( NaturalDocs::Project->DataFile('PreviousSettings.nd'),
1320                                                                            NaturalDocs::Version->FromString('1.52') ))
1321             {  $fileIsOkay = 1;  };
1322         };
1323
1324     if (!$fileIsOkay)
1325         {
1326         # We need to reparse everything because --documented-only may have changed.
1327         # We need to rebuild everything because --tab-length may have changed.
1328         NaturalDocs::Project->ReparseEverything();
1329         NaturalDocs::Project->RebuildEverything();
1330         }
1331     else
1332         {
1333         my $raw;
1334
1335         # [UInt8: tab expansion]
1336         # [UInt8: documented only (0 or 1)]
1337         # [UInt8: no auto-group (0 or 1)]
1338         # [UInt8: only file titles (0 or 1)]
1339         # [UInt8: highlight code (0 or 1)]
1340         # [UInt8: highlight anonymous (0 or 1)]
1341
1342         my $prevTabLength = NaturalDocs::BinaryFile->GetUInt8();
1343         my $prevDocumentedOnly = NaturalDocs::BinaryFile->GetUInt8();
1344         my $prevNoAutoGroup = NaturalDocs::BinaryFile->GetUInt8();
1345         my $prevOnlyFileTitles = NaturalDocs::BinaryFile->GetUInt8();
1346         my $prevHighlightCode = NaturalDocs::BinaryFile->GetUInt8();
1347         my $prevHighlightAnonymous = NaturalDocs::BinaryFile->GetUInt8();
1348
1349         if ($prevDocumentedOnly == 0)
1350             {  $prevDocumentedOnly = undef;  };
1351         if ($prevNoAutoGroup == 0)
1352             {  $prevNoAutoGroup = undef;  };
1353         if ($prevOnlyFileTitles == 0)
1354             {  $prevOnlyFileTitles = undef;  };
1355         if ($prevHighlightCode == 0)
1356             {  $prevHighlightCode = undef;  };
1357         if ($prevHighlightAnonymous == 0)
1358             {  $prevHighlightAnonymous = undef;  };
1359
1360         if ($prevTabLength != $self->TabLength() ||
1361                 $prevHighlightCode != $self->HighlightCode() ||
1362                 $prevHighlightAnonymous != $self->HighlightAnonymous())
1363             {
1364             NaturalDocs::Project->RebuildEverything();
1365             };
1366
1367         if ($prevDocumentedOnly != $self->DocumentedOnly() ||
1368             $prevNoAutoGroup != $self->NoAutoGroup() ||
1369             $prevOnlyFileTitles != $self->OnlyFileTitles())
1370             {
1371             NaturalDocs::Project->ReparseEverything();
1372             };
1373
1374
1375         # [UInt8: number of input directories]
1376
1377         my $inputDirectoryCount = NaturalDocs::BinaryFile->GetUInt8();
1378
1379         while ($inputDirectoryCount)
1380             {
1381             # [UString16: input directory] [UString16: input directory name] ...
1382
1383             my $inputDirectory = NaturalDocs::BinaryFile->GetUString16();
1384             my $inputDirectoryName = NaturalDocs::BinaryFile->GetUString16();
1385
1386             # Not doing anything with this for now.
1387
1388             $inputDirectoryCount--;
1389             };
1390
1391
1392         # [UInt8: number of output targets]
1393
1394         my $outputTargetCount = NaturalDocs::BinaryFile->GetUInt8();
1395
1396         # Keys are the directories, values are the command line options.
1397         my %previousOutputDirectories;
1398
1399         while ($outputTargetCount)
1400             {
1401             # [UString16: output directory] [UString16: output format command line option] ...
1402
1403             my $outputDirectory = NaturalDocs::BinaryFile->GetUString16();
1404             my $outputCommand = NaturalDocs::BinaryFile->GetUString16();
1405
1406             $previousOutputDirectories{$outputDirectory} = $outputCommand;
1407
1408             $outputTargetCount--;
1409             };
1410
1411         # Check if any targets were added to the command line, or if their formats changed.  We don't care if targets were
1412         # removed.
1413         my $buildTargets = $self->BuildTargets();
1414
1415         foreach my $buildTarget (@$buildTargets)
1416             {
1417             if (!exists $previousOutputDirectories{$buildTarget->Directory()} ||
1418                 $buildTarget->Builder()->CommandLineOption() ne $previousOutputDirectories{$buildTarget->Directory()})
1419                 {
1420                 NaturalDocs::Project->RebuildEverything();
1421                 last;
1422                 };
1423             };
1424
1425         NaturalDocs::BinaryFile->Close();
1426         };
1427     };
1428
1429
1430 #
1431 #   Function: SavePreviousSettings
1432 #
1433 #   Saves the settings into <PreviousSettings.nd>.
1434 #
1435 sub SavePreviousSettings
1436     {
1437     my ($self) = @_;
1438
1439     NaturalDocs::BinaryFile->OpenForWriting(  NaturalDocs::Project->DataFile('PreviousSettings.nd') );
1440
1441     # [UInt8: tab length]
1442     # [UInt8: documented only (0 or 1)]
1443     # [UInt8: no auto-group (0 or 1)]
1444     # [UInt8: only file titles (0 or 1)]
1445     # [UInt8: highlight code (0 or 1)]
1446     # [UInt8: highlight anonymous (0 or 1)]
1447     # [UInt8: number of input directories]
1448
1449     my $inputDirectories = $self->InputDirectories();
1450
1451     NaturalDocs::BinaryFile->WriteUInt8($self->TabLength());
1452     NaturalDocs::BinaryFile->WriteUInt8($self->DocumentedOnly() ? 1 : 0);
1453     NaturalDocs::BinaryFile->WriteUInt8($self->NoAutoGroup() ? 1 : 0);
1454     NaturalDocs::BinaryFile->WriteUInt8($self->OnlyFileTitles() ? 1 : 0);
1455     NaturalDocs::BinaryFile->WriteUInt8($self->HighlightCode() ? 1 : 0);
1456     NaturalDocs::BinaryFile->WriteUInt8($self->HighlightAnonymous() ? 1 : 0);
1457     NaturalDocs::BinaryFile->WriteUInt8(scalar @$inputDirectories);
1458
1459     foreach my $inputDirectory (@$inputDirectories)
1460         {
1461         my $inputDirectoryName = $self->InputDirectoryNameOf($inputDirectory);
1462
1463         # [UString16: input directory] [UString16: input directory name] ...
1464         NaturalDocs::BinaryFile->WriteUString16($inputDirectory);
1465         NaturalDocs::BinaryFile->WriteUString16($inputDirectoryName);
1466         };
1467
1468     # [UInt8: number of output targets]
1469
1470     my $buildTargets = $self->BuildTargets();
1471     NaturalDocs::BinaryFile->WriteUInt8(scalar @$buildTargets);
1472
1473     foreach my $buildTarget (@$buildTargets)
1474         {
1475         # [UString16: output directory] [UString16: output format command line option] ...
1476         NaturalDocs::BinaryFile->WriteUString16( $buildTarget->Directory() );
1477         NaturalDocs::BinaryFile->WriteUString16( $buildTarget->Builder()->CommandLineOption() );
1478         };
1479
1480     NaturalDocs::BinaryFile->Close();
1481     };
1482
1483
1484 1;