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