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