]> git.sur5r.net Git - cc65/blob - src/ca65html/ca65html
Fixed generation of invalid HTML code
[cc65] / src / ca65html / ca65html
1 #!/usr/bin/perl
2 ###############################################################################
3 #                                                                             #
4 #                                   main.c                                    #
5 #                                                                             #
6 #                      Convert a ca65 source into HTML                        #
7 #                                                                             #
8 #                                                                             #
9 #                                                                             #
10 #  (C) 2000      Ullrich von Bassewitz                                        #
11 #                Wacholderweg 14                                              #
12 #                D-70597 Stuttgart                                            #
13 #  EMail:        uz@musoftware.de                                             #
14 #                                                                             #
15 #                                                                             #
16 #  This software is provided 'as-is', without any expressed or implied        #
17 #  warranty.  In no event will the authors be held liable for any damages     #
18 #  arising from the use of this software.                                     #
19 #                                                                             #
20 #  Permission is granted to anyone to use this software for any purpose,      #
21 #  including commercial applications, and to alter it and redistribute it     #
22 #  freely, subject to the following restrictions:                             #
23 #                                                                             #
24 #  1. The origin of this software must not be misrepresented; you must not    #
25 #     claim that you wrote the original software. If you use this software    #
26 #     in a product, an acknowledgment in the product documentation would be   #
27 #     appreciated but is not required.                                        #
28 #  2. Altered source versions must be plainly marked as such, and must not    #
29 #     be misrepresented as being the original software.                       #
30 #  3. This notice may not be removed or altered from any source               #
31 #     distribution.                                                           #
32 #                                                                             #
33 ###############################################################################
34
35
36
37 use strict 'vars';
38 use warnings;
39
40 # Modules
41 use Getopt::Long;
42
43
44
45 #-----------------------------------------------------------------------------#
46 #                                  Variables                                  #
47 # ----------------------------------------------------------------------------#
48
49
50
51 # Global variables
52 my %Files       = ();           # List of all files.
53 my $FileCount   = 0;            # Number of input files
54 my %Exports     = ();           # List of exported symbols.
55 my %Imports     = ();           # List of imported symbols.
56 my %Labels      = ();           # List of all labels
57 my $LabelNum    = 0;            # Counter to generate unique labels
58
59 # Command line options
60 my $BGColor     = "#FFFFFF";    # Background color
61 my $Debug       = 0;            # No debugging
62 my $Help        = 0;            # Help flag
63 my $HTMLDir     = "";           # Directory in which to create the files
64 my $IndexCols   = 6;            # Columns in the file listing
65 my $IndexTitle  = "Index";      # Title of index page
66 my $IndexName   = "index.html"; # Name of index page
67 my $IndexPage   = 0;            # Create an index page
68 my $LinkStyle   = 0;            # Default link style
69 my $ReplaceExt  = 0;            # Replace extension instead of appending
70 my $TextColor   = "#000000";    # Text color
71 my $Verbose     = 0;            # Be quiet
72
73 # Table used to convert the label number into names
74 my @NameTab     = ("A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K",
75                    "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V",
76                    "W", "X", "Y", "Z", "0", "1", "2", "3", "4", "5", "6",
77                    "7", "8", "9");
78
79
80
81 #-----------------------------------------------------------------------------#
82 #                              Helper functions                               #
83 # ----------------------------------------------------------------------------#
84
85
86
87 # Terminate with an error
88 sub Abort {
89     print STDERR "ca65html: @_\n";
90     exit 1;
91 }
92
93 # Print a message if verbose is true
94 sub Gabble {
95     if ($Verbose) {
96         print "ca65html: @_\n";
97     }
98 }
99
100 # Generate a label and return it
101 sub GenLabel {
102
103     my $I;
104     my $L = "";;
105     my $Num = $LabelNum++;
106
107     # Generate the label
108     for ($I = 0; $I < 4; $I++) {
109         $L = $NameTab[$Num % 36] . $L;
110         $Num /= 36;
111     }
112     return $L;
113 }
114
115 # Make an output file name from an input file name
116 sub GetOutName {
117
118     # Input name is parameter
119     my $InName = $_[0];
120
121     # Create the output file name from the input file name
122     if ($ReplaceExt && $InName =~ /^(.+)\.([^\.\/]*)$/) {
123         return "$1.html";
124     } else {
125         return "$InName.html";
126     }
127 }
128
129 # Remove illegal characters from a string
130 sub Cleanup {
131     my $S = shift (@_);
132     $S =~ s/&/&amp;/g;
133     $S =~ s/</&lt;/g;
134     $S =~ s/>/&gt;/g;
135     $S =~ s/\"/&quot;/g;
136     return $S;
137 }
138
139 # Strip a path from a filename and return just the name
140 sub StripPath {
141
142     # Filename is argument
143     my $FileName = $_[0];
144
145     # Remove a path name if we have one
146     $FileName =~ /^(.*?)([^\/]*)$/;
147     return $2;
148 }
149
150
151
152 #-----------------------------------------------------------------------------#
153 #                         Document header and footer                          #
154 # ----------------------------------------------------------------------------#
155
156
157
158 # Print the document header
159 sub DocHeader {
160     my $OUT = shift (@_);
161     my $Asm = shift (@_);
162     print $OUT <<"EOF";
163 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html-40/loose.dtd">
164 <html>
165 <head>
166 <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
167 <meta name="GENERATOR" content="ca65html">
168 <title>$Asm</title>
169 </head>
170 <body bgcolor="$BGColor" text="$TextColor" link="#0000d0" vlink="#000060" alink="#00d0d0">
171 <p><br><p>
172 <center><h1>$Asm</h1></center>
173 <hr><p><br><p>
174 <pre>
175 EOF
176 }
177
178 # Print the document footer
179 sub DocFooter {
180     my $OUT  = shift (@_);
181     my $Name = shift (@_);
182
183     # Get the current date and time
184     my $Today = localtime;
185
186     print $OUT <<"EOF";
187 </pre>
188 <p><br><p>
189 <hr size=1 noshade>
190 <address>
191 <a href="http://validator.w3.org/check/referer"><img border=0 src="http://validator.w3.org/images/vh40" alt="Valid HTML 4.0!" height=31 width=88 align=right></a>
192 $Name; generated on $Today by ca65html<br>
193 <a href=\"mailto:uz\@cc65.org\">uz\@cc65.org</a>
194 </address>
195 </body>
196 </html>
197 EOF
198 }
199
200
201
202 #-----------------------------------------------------------------------------#
203 #                            File list management                             #
204 # ----------------------------------------------------------------------------#
205
206
207
208 sub AddFile {
209
210     # Argument is file to add
211     my $FileName = $_[0];
212
213     # Get just the name (remove a path if there is one)
214     my $Name = StripPath ($FileName);
215
216     # Check if we have the file already
217     if (exists ($Files{$Name})) {
218         Gabble ("File \"$FileName\" already known");
219         return;
220     }
221
222     # Check with the full pathname. If we don't find it, search in the current
223     # directory
224     if (-f $FileName && -r $FileName) {
225         $Files{$Name} = $FileName;
226         $FileCount++;
227     } elsif (-f $Name && -r $Name) {
228         $Files{$Name} = $Name;
229         $FileCount++;
230     } else {
231         Abort ("$FileName not found or not readable");
232     }
233 }
234
235
236
237 #-----------------------------------------------------------------------------#
238 #                       Referencing and defining labels                       #
239 # ----------------------------------------------------------------------------#
240
241
242
243 # Get a label reference
244 sub RefLabel {
245
246     # Arguments are: Filename, identifier, item that should be tagged
247     my $FileName = $_[0];
248     my $Id       = $_[1];
249     my $Item     = $_[2];
250
251     # Search for the identifier in the list of labels
252     if (exists ($Labels{$FileName}{$Id})) {
253         # It is a label (in this file)
254         return sprintf ("<a href=\"#%s\">%s</a>", $Labels{$FileName}{$Id}, $Item);
255     } elsif (exists ($Imports{$FileName}{$Id})) {
256         # It is an import. If LinkStyle is 1, or if the file exporting the
257         # identifier is not visible, we link to the .import statement in the
258         # current file. Otherwise we link directly to the referenced symbol
259         # in the file that exports it.
260         if ($LinkStyle == 1 or not exists ($Exports{$Id})) {
261             return sprintf ("<a href=\"#%s\">%s</a>", $Imports{$FileName}{$Id}, $Item);
262         } else {
263             # Get the filename from the export
264             my $Label;
265             ($FileName, $Label) = split (/#/, $Exports{$Id});
266             if (not defined ($Labels{$FileName}{$Id})) {
267                 # This may currently happen because we don't see .include
268                 # statements, so we may have an export but no definition.
269                 # Link to the .export statement instead
270                 $Label = $Exports{$Id};
271             } else {
272                 # Link to the definition in the file
273                 $Label = sprintf ("%s#%s", $FileName, $Labels{$FileName}{$Id});
274             }
275             return sprintf ("<a href=\"%s\">%s</a>", $Label, $Item);
276         }
277     } else {
278         # The symbol is unknown, return as is
279         return $Item;
280     }
281 }
282
283
284
285 #-----------------------------------------------------------------------------#
286 #                                   Pass 1                                    #
287 # ----------------------------------------------------------------------------#
288
289
290
291 # Process1: Read one file for the first time.
292 sub Process1 {
293
294     # Variables
295     my $Line;
296     my $Id;
297
298     # Filename is parameter
299     my $InName = shift(@_);
300
301     # Create the output file name from the input file name
302     my $OutName = GetOutName ($InName);
303
304     # Current cheap local label prefix is empty
305     my $CheapPrefix = "";
306
307     # Open a the input file
308     my $FileName = $Files{$InName};     # Includes path if needed
309     open (INPUT, "<$FileName") or Abort ("Cannot open $FileName: $!");
310
311     # Keep the user happy
312     Gabble ("$FileName => $OutName");
313
314     # Read and process all lines from the file
315     while ($Line = <INPUT>) {
316
317         # Remove the newline
318         chop ($Line);
319
320         # Check for a label
321         if ($Line =~ /^\s*(\@?)([_a-zA-Z][_\w]*)\s*(:|=)/) {
322
323             # Is this a local label?
324             if ($1 eq "\@") {
325                 # Use the prefix
326                 $Id = "$CheapPrefix$1$2";
327             } else {
328                 # Use as is
329                 $Id = $2;
330                 # Remember the id as new cheap local prefix
331                 $CheapPrefix = $Id;
332             }
333
334             # Remember the label
335             $Labels{$OutName}{$Id} = GenLabel();
336
337         # Check for an import statement
338         } elsif ($Line =~ /^\s*(\.import|\.importzp)\s+(.*?)(\s*)(;.*$|$)/) {
339
340             # Split into a list of identifiers
341             my @Ids = split (/\s*,\s*/, $2);
342             for $Id (@Ids) {
343                 $Imports{$OutName}{$Id} = GenLabel();
344             }
345
346         # Check for an export statement
347         } elsif ($Line =~ /^\s*(\.export|\.exportzp)\s+(.*?)(\s*)(;.*$|$)/) {
348
349             # Split into a list of identifiers
350             my @Ids = split (/\s*,\s*/, $2);
351             for $Id (@Ids) {
352                 $Exports{$Id} = sprintf ("%s#%s", $OutName, GenLabel());
353             }
354
355         # Check for a .proc statement
356         } elsif ($Line =~ /^\s*\.proc\s+([_a-zA-Z][_\w]*)?.*$/) {
357
358             # Do we have an id?
359             $Id = $1;
360             if ($Id ne "") {
361                 $Labels{$OutName}{$Id} = GenLabel();
362             }
363
364         }
365     }
366
367     # Close the input file
368     close (INPUT);
369 }
370
371
372
373 # Pass1: Read all files for the first time.
374 sub Pass1 () {
375
376     # Keep the user happy
377     Gabble ("Pass 1");
378
379     # Walk over the files
380     for my $InName (keys (%Files)) {
381         # Process one file
382         Process1 ($InName);
383     }
384 }
385
386
387
388 #-----------------------------------------------------------------------------#
389 #                                   Pass 2                                    #
390 # ----------------------------------------------------------------------------#
391
392
393
394 # Process2: Read one file the second time.
395 sub Process2 {
396
397     # Variables
398     my $Base;
399     my $Ext;
400     my $Line;
401     my $OutLine;
402     my $Id;
403     my $Label;
404     my $Operand;
405     my $Comment;
406
407     # Input file is parameter
408     my $InName = shift(@_);
409
410     # Create the output file name from the input file name
411     my $OutName = GetOutName ($InName);
412
413     # Current cheap local label prefix is empty
414     my $CheapPrefix = "";
415
416     # Open a the input file
417     my $FileName = $Files{$InName};     # Includes path if needed
418     open (INPUT, "<$FileName") or Abort ("Cannot open $FileName: $!");
419
420     # Open the output file and print the HTML header
421     open (OUTPUT, ">$HTMLDir$OutName") or Abort ("Cannot open $OutName: $!");
422     DocHeader (OUTPUT, $InName);
423
424     # Keep the user happy
425     Gabble ("$FileName => $OutName");
426
427     # The instructions that will have hyperlinks if a label is used
428     my $Ins = "adc|add|and|bcc|bcs|beq|bit|bmi|bne|bpl|bcv|bra|bvs|".
429               "cmp|cpx|cpy|dec|eor|inc|jmp|jsr|lda|ldx|ldy|ora|rol|".
430               "sbc|sta|stx|sty|sub|";
431
432     # Read the input file, replacing references by hyperlinks and mark
433     # labels as link targets.
434     while ($Line = <INPUT>) {
435
436         # Remove the newline
437         chop ($Line);
438
439         # Clear the output line
440         $OutLine = "";
441
442         # Check for a label. If we have one, process it and remove it
443         # from the line
444         if ($Line =~ /^\s*?(\@?)([_a-zA-Z][_\w]*)(\s*)(:|=)(.*)$/) {
445
446             # Is this a local label?
447             if ("$1" eq "\@") {
448                 # Use the prefix
449                 $Id = "$CheapPrefix$1$2";
450             } else {
451                 # Use as is
452                 $Id = $2;
453                 # Remember the id as new cheap local prefix
454                 $CheapPrefix = $Id;
455             }
456
457             # Get the label for the id
458             $Label = $Labels{$OutName}{$Id};
459
460             # Print the label with a tag
461             $OutLine .= sprintf ("<a name=\"%s\">%s%s</a>%s%s", $Label, $1, $2, $3, $4);
462
463             # Use the remainder for line
464             $Line = $5;
465         }
466
467         # Print any leading whitespace and remove it, so we don't have to
468         # care about whitespace below.
469         if ($Line =~ /^(\s+)(.*)$/) {
470             $OutLine .= "$1";
471             $Line = $2;
472         }
473
474         # Handle the import statements
475         if ($Line =~ /^(\.import|\.importzp)(\s+)(.*)$/) {
476
477             # Print any fixed stuff from the line and remove it
478             $OutLine .= "$1$2";
479             $Line = $3;
480
481             # Print all identifiers if there are any
482             while ($Line =~ /^([_a-zA-Z][_\w]*)(.*)$/) {
483
484                 # Identifier is $1, remainder is $2
485                 $Id = $1;
486                 $Line = $2;
487
488                 # Variable to assemble HTML representation
489                 my $Contents = "";
490
491                 # Make this import a link target
492                 if (exists ($Imports{$OutName}{$Id})) {
493                     $Label = $Imports{$OutName}{$1};
494                     $Contents .= sprintf (" name=\"%s\"", $Label);
495                 }
496
497                 # If we have an export for this import, add a link to this
498                 # export definition
499                 if (exists ($Exports{$Id})) {
500                     $Label = $Exports{$Id};
501                     $Contents .= sprintf (" href=\"%s\"", $Label);
502                 }
503
504                 # Add the HTML stuff to the output line
505                 if ($Contents ne "") {
506                     $OutLine .= sprintf ("<a%s>%s</a>", $Contents, $Id);
507                 } else {
508                     $OutLine .= $Id;
509                 }
510
511                 # Check if another identifier follows
512                 if ($Line =~ /^(\s*),(\s*)(.*)$/) {
513                     $OutLine .= "$1,$2";
514                     $Line = $3;
515                 } else {
516                     last;
517                 }
518             }
519
520             # Add an remainder if there is one
521             $OutLine .= Cleanup ($Line);
522
523         # Handle export statements
524         } elsif ($Line =~ /^(\.export|\.exportzp)(\s+)(.*)$/) {
525
526             # Print the command the and white space
527             $OutLine .= "$1$2";
528             $Line = $3;
529
530             # Print all identifiers if there are any
531             while ($Line =~ /^([_a-zA-Z][_\w]*)(.*)$/) {
532
533                 # Identifier is $1, remainder is $2
534                 $Id = $1;
535                 $Line = $2;
536
537                 # Variable to assemble HTML representation
538                 my $Contents = "";
539
540                 # If we have a definition for this export in this file, add
541                 # a link to the definition.
542                 if (exists ($Labels{$OutName}{$1})) {
543                     $Label = $Labels{$OutName}{$1};
544                     $Contents = sprintf (" href=\"#%s\"", $Label);
545                 }
546
547                 # If we have this identifier in the list of exports, add a
548                 # jump target for the export.
549                 if (exists ($Exports{$Id})) {
550                     $Label = $Exports{$Id};
551                     # Be sure to use only the label part
552                     $Label =~ s/^(.*#)(.*)$/$2/;        # ##FIXME: Expensive
553                     $Contents .= sprintf (" name=\"%s\"", $Label);
554                 }
555
556                 # Add the HTML stuff to the output line
557                 if ($Contents ne "") {
558                     $OutLine .= sprintf ("<a%s>%s</a>", $Contents, $Id);
559                 } else {
560                     $OutLine .= $Id;
561                 }
562
563                 # Check if another identifier follows
564                 if ($Line =~ /^(\s*),(\s*)(.*)$/) {
565                     $OutLine .= "$1,$2";
566                     $Line = $3;
567                 } else {
568                     last;
569                 }
570             }
571
572             # Add an remainder if there is one
573             $OutLine .= Cleanup ($Line);
574
575         # Check for .addr and .word
576         } elsif ($Line =~ /^(\.addr|\.word)(\s+)(.*)$/) {
577
578             # Print the command the and white space
579             $OutLine .= "$1$2";
580             $Line = $3;
581
582             # Print all identifiers if there are any
583             while ($Line =~ /^([_a-zA-Z][_\w]*)(.*)$/) {
584                 if (exists ($Labels{$OutName}{$1})) {
585                     $Label = $Labels{$OutName}{$1};
586                     $OutLine .= sprintf ("<a href=\"#%s\">%s</a>", $Label, $1);
587                 } else {
588                     $OutLine .= "$1";
589                 }
590                 $Line = $2;
591                 if ($Line =~ /^(\s*),(\s*)(.*)$/) {
592                     $OutLine .= "$1,$2";
593                     $Line = $3;
594                 } else {
595                     last;
596                 }
597             }
598
599             # Add an remainder if there is one
600             $OutLine .= Cleanup ($Line);
601
602         # Handle .proc
603         } elsif ($Line =~ /^(\.proc\s+)([_a-zA-Z][_\w]*)?(.*)$/) {
604
605             # Do we have an identifier?
606             if ($2 ne "") {
607                 # Get the label for the id
608                 $Label = $Labels{$OutName}{$2};
609
610                 # Print the label with a tag
611                 $OutLine .= sprintf ("%s<a name=\"%s\">%s</a>", $1, $Label, $2);
612
613                 # Use the remainder for line
614                 $Line = $3;
615             }
616
617             # Cleanup the remainder and add it
618             $OutLine .= Cleanup ($Line);
619
620         # Handle .include
621         } elsif ($Line =~ /^(\.include)(\s+)\"((?:[^\"]+?|\\\")+)\"(\s*)(;.*$|$)/) {
622
623             # Add the fixed stuff to the output line
624             $OutLine .= "$1$2\"";
625
626             # Get the filename into a named variable
627             my $FileName = $3;
628
629             # Remember the remainder
630             $Line = "\"$4$5";
631
632             # Get the name without a path
633             my $Name = StripPath ($FileName);
634
635             # We don't need FileName any longer as is, so clean it up
636             $FileName = Cleanup ($FileName);
637
638             # If the include file is among the list of our files, add a link,
639             # otherwise just add the name as is.
640             if (exists ($Files{$Name})) {
641                 $OutLine .= sprintf ("<a href=\"%s\">%s</a>", GetOutName ($Name), $FileName);
642             } else {
643                 $OutLine .= $FileName;
644             }
645
646             # Add the remainder
647             $OutLine .= Cleanup ($Line);
648
649         # Check for any legal instruction
650         } elsif ($Line =~ /^($Ins)(\s+)(.*?)(\s*)(;.*$|$)/) {
651
652             # Print the instruction and white space
653             $OutLine .= "$1$2";
654
655             # Remember the remaining parts
656             $Operand = $3;
657             $Comment = Cleanup ("$4$5");
658
659             # Check for the first identifier in the operand and replace it
660             # by a hyperlink
661             if ($Operand =~ /^([^_a-zA-Z]*?)(\@?)([_a-zA-Z][_\w]*)(.*)$/) {
662
663                 # Is this a local label?
664                 if ("$2" eq "\@") {
665                     # Use the prefix
666                     $Id = "$CheapPrefix$2$3";
667                 } else {
668                     # Use as is
669                     $Id = $3;
670                 }
671
672                 # Get the reference to this label if we find it
673                 $Operand = Cleanup($1) . RefLabel($OutName, $Id, $2 . $3) . Cleanup($4);
674             }
675
676             # Reassemble and print the line
677             $OutLine .= "$Operand$Comment";
678
679         } else {
680
681             # Nothing known - print the line
682             $OutLine .= Cleanup ($Line);
683
684         }
685
686         # Print the result
687         print OUTPUT "$OutLine\n";
688     }
689
690     # Print the HTML footer
691     DocFooter (OUTPUT, $OutName);
692
693     # Close the files
694     close (INPUT);
695     close (OUTPUT);
696 }
697
698
699
700 # Pass2: Read all files the second time.
701 sub Pass2 () {
702
703     # Keep the user happy
704     Gabble ("Pass 2");
705
706     # Walk over the files
707     for my $InName (keys (%Files)) {
708         # Process one file
709         Process2 ($InName);
710     }
711 }
712
713
714
715 #-----------------------------------------------------------------------------#
716 #                            Create an index page                             #
717 # ----------------------------------------------------------------------------#
718
719
720
721 # Print a list of all files
722 sub FileIndex {
723
724     # File is argument
725     my $INDEX = $_[0];
726
727     # Print the file list in a table
728     print $INDEX "<h2>Files</h2><p>\n";
729     print $INDEX "<table border=\"0\" width=\"100%\">\n";
730     my $Count = 0;
731     for my $File (sort (keys (%Files))) {
732
733         #
734         if (($Count % $IndexCols) == 0) {
735             print $INDEX "<tr>\n";
736         }
737         printf $INDEX "<td><a href=\"%s\">%s</a></td>\n", GetOutName ($File), $File;
738         if (($Count % $IndexCols) == $IndexCols-1) {
739             print $INDEX "</tr>\n";
740         }
741         $Count++;
742     }
743     if (($Count % $IndexCols) != 0) {
744         print $INDEX "</tr>\n";
745     }
746     print $INDEX "</table><p><br><p>\n";
747 }
748
749
750
751 # Print a list of all exports
752 sub ExportIndex {
753
754     # File is argument
755     my $INDEX = $_[0];
756
757     # Print the file list in a table
758     print $INDEX "<h2>Exports</h2><p>\n";
759     print $INDEX "<table border=\"0\" width=\"100%\">\n";
760     my $Count = 0;
761     for my $Export (sort (keys (%Exports))) {
762
763         # Get the export
764         my $File;
765         my $Label;
766         ($File, $Label) = split (/#/, $Exports{$Export});
767
768         # The label is the label of the export statement. If we can find the
769         # actual label, use this instead.
770         if (exists ($Labels{$File}{$Export})) {
771             $Label = $Labels{$File}{$Export};
772         }
773
774         #
775         if (($Count % $IndexCols) == 0) {
776             print $INDEX "<tr>\n";
777         }
778         printf $INDEX "<td><a href=\"%s#%s\">%s</a></td>\n", $File, $Label, $Export;
779         if (($Count % $IndexCols) == $IndexCols-1) {
780             print $INDEX "</tr>\n";
781         }
782         $Count++;
783     }
784     if (($Count % $IndexCols) != 0) {
785         print $INDEX "</tr>\n";
786     }
787     print $INDEX "</table><p><br><p>\n";
788 }
789
790
791
792 sub CreateIndex {
793
794     # Open the index page file
795     open (INDEX, ">$HTMLDir$IndexName") or Abort ("Cannot open $IndexName: $!");
796
797     # Print the header
798     print INDEX <<"EOF";
799 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html-40/loose.dtd">
800 <html>
801 <head>
802 <meta http-equiv="Content-Type" content=\"text/html; charset=iso-8859-1">
803 <meta name="GENERATOR" content="ca65html">
804 <title>$IndexTitle</title>
805 </head>
806 <body bgcolor="$BGColor" text="$TextColor" link="#0000d0" vlink="#000060" alink="#00d0d0">
807 <p><br><p>
808 <center><h1>$IndexTitle</h1></center>
809 <hr><p><br><p>
810 EOF
811
812     # Print the file list in a table
813     FileIndex (INDEX);
814     ExportIndex (INDEX);
815
816     # Print the document footer
817     DocFooter (INDEX, $IndexName);
818
819     # Close the index file
820     close (INDEX);
821 }
822
823
824
825 #-----------------------------------------------------------------------------#
826 #                           Print usage information                           #
827 # ----------------------------------------------------------------------------#
828
829
830
831 sub Usage {
832     print "Usage: ca65html [options] file ...\n";
833     print "Options:\n";
834     print "  --bgcolor color     Use background color c instead of $BGColor\n";
835     print "  --help              This text\n";
836     print "  --htmldir dir       Specify directory for HTML files\n";
837     print "  --indexcols n       Use n columns on index page (default $IndexCols)\n";
838     print "  --indexname file    Use file for the index file instead of $IndexName\n";
839     print "  --indexpage         Create an index page\n";
840     print "  --indextitle title  Use title as the index title instead of $IndexTitle\n";
841     print "  --linkstyle style   Use the given link style\n";
842     print "  --replaceext        Replace source extension instead of appending .html\n";
843     print "  --textcolor color   Use text color c instead of $TextColor\n";
844     print "  --verbose           Be more verbose\n";
845 }
846
847
848
849 #-----------------------------------------------------------------------------#
850 #                                    Main                                     #
851 # ----------------------------------------------------------------------------#
852
853
854
855 # Get program options
856 GetOptions ("bgcolor=s"         => \$BGColor,
857             "debug!"            => \$Debug,
858             "help"              => \$Help,
859             "htmldir=s"         => \$HTMLDir,
860             "indexcols=i"       => \$IndexCols,
861             "indexname=s"       => \$IndexName,
862             "indexpage"         => \$IndexPage,
863             "indextitle=s"      => \$IndexTitle,
864             "linkstyle=i"       => \$LinkStyle,
865             "replaceext"        => \$ReplaceExt,
866             "textcolor=s"       => \$TextColor,
867             "verbose!"          => \$Verbose,
868             "<>"                => \&AddFile);
869
870 # Check some arguments
871 if ($IndexCols <= 0 || $IndexCols >= 20) {
872     Abort ("Invalid value for --indexcols option");
873 }
874 if ($HTMLDir ne "" && $HTMLDir =~ /[^\/]$/) {
875     # Add a trailing path separator
876     $HTMLDir .= "/";
877 }
878
879
880
881 # Print help if requested
882 if ($Help) {
883     Usage ();
884 }
885
886 # Check if we have input files given
887 if ($FileCount == 0) {
888     Abort ("No input files");
889 }
890
891 # Convert the documents
892 Pass1 ();
893 Pass2 ();
894
895 # Generate an index page if requested
896 if ($IndexPage) {
897     CreateIndex ();
898 }
899
900 # Done
901 exit 0;
902