3 # sfdc - Compile SFD files into someting useful
4 # Copyright (C) 2003-2004 Martin Blom <martin@blom.org>
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; either version 2
9 # of the License, or (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25 # The default AmigaOS GG installation of does not seem to include
26 # Pod::Usage, so we have to provide a fallback. Ugly, but it works and
36 # Minimal fall-back ...
44 my $output = \*STDERR;
48 /^-verbose$/ && do { $verbose = shift @params};
49 /^-exitval$/ && do { $exitval = shift @params};
50 /^-message$/ && do { $message = shift @params};
51 /^-output$/ && do { $output = shift @params};
55 print $output "$message\n" if $message;
57 print $output "Perl module Pod::Usage is missing.\n";
58 print $output "Please refer to the sfdc documentation for usage, ".
59 "or install Pod::Usage.\n";
66 sub parse_proto
( $$$ );
67 sub open_output
( $$ );
68 sub will_close_output
( $$ );
73 'struct Library* LibInit(struct Library* library,' .
75 ' struct ExecBase* SysBase)' .
77 'struct Library* LibOpen(ULONG version) (d0)',
79 'BPTR LibExpunge() ()',
85 'struct Library* DevInit(struct Library* library,' .
87 ' struct ExecBase* SysBase)' .
89 'ULONG DevOpen(struct IORequest* ioreq,' .
91 ' ULONG flags) (a1,d0,d1)',
92 'BPTR DevClose(struct IORequest* ioreq) (a1)',
93 'BPTR DevExpunge() ()',
95 'VOID DevBeginIO(struct IORequest* ioreq) (a1)',
96 'ULONG DevAbortIO(struct IORequest* ioreq) (a1)'
101 'struct ClassLibrary* ClassInit(struct ClassLibrary* library,' .
103 ' struct ExecBase* SysBase)' .
105 'struct ClassLibrary* ClassOpen(ULONG version) (d0)',
106 'BPTR ClassClose() ()',
107 'BPTR ClassExpunge() ()',
108 'ULONG ClassNull() ()',
109 'Class* ObtainEngine() ()',
114 { target
=> 'generic',
115 vectors
=> { 'library' => @lf, 'device' => @df, 'boopsi' => @bf },
121 '(\w)+(-.*)?-aros' =>
123 vectors
=> { 'library' => @lf, 'device' => @df, 'boopsi' => @bf },
124 macros
=> 'MacroAROS',
126 gatestubs
=> 'GateAROS',
127 sdistubs
=> 'SDIAROS'
130 'i.86be(-pc)?-amithlon' =>
131 { target
=> 'amithlon',
132 vectors
=> { 'library' => @lf, 'device' => @df, 'boopsi' => @bf },
134 stubs
=> 'StubAmithlon',
135 gatestubs
=> 'GateAmithlon'
138 'm68k(-unknown)?-amigaos' =>
139 { target
=> 'amigaos',
140 vectors
=> { 'library' => @lf, 'device' => @df, 'boopsi' => @bf },
141 macros
=> 'Macro68k',
143 gatestubs
=> 'Gate68k'
146 'p(ower)?pc(-unknown)?-amigaos' =>
147 { target
=> 'amigaos4',
148 vectors
=> { 'library' => @lf, 'device' => @df, 'boopsi' => @bf },
149 macros
=> 'MacroAOS4',
151 gatestubs
=> 'GateAOS4'
154 'p(ower)?pc(-unknown)?-morphos' =>
155 { target
=> 'morphos',
156 vectors
=> { 'library' => @lf, 'device' => @df, 'boopsi' => @bf },
157 macros
=> 'MacroMOS',
159 gatestubs
=> 'GateMOS'
165 ###############################################################################
166 ### Main program ##############################################################
167 ###############################################################################
169 Getopt
::Long
::Configure
("bundling");
175 my $addvectors = 'none';
180 my $target = 'm68k-unknown-amigaos';
183 GetOptions
('addvectors=s' => \
$addvectors,
184 'gateprefix=s' => \
$gateprefix,
186 'libarg=s' => \
$libarg,
187 'libprefix=s' => \
$libprefix,
190 'output|o=s' => \
$output,
191 'quiet|q' => \
$quiet,
192 'target=s' => \
$target,
193 'version|v' => \
$version) or exit 10;
196 print STDERR
"sfdc SFDC_VERSION (SFDC_DATE)\n";
197 print STDERR
"Copyright (C) 2003-2004 Martin Blom <martin\@blom.org>\n";
198 print STDERR
"This is free software; " .
199 "see the source for copying conditions.\n";
204 pod2usage
(-verbose
=> 1,
206 -output
=> \
*STDOUT
);
210 pod2usage
(-verbose
=> 3,
216 pod2usage
(-message
=> "No SFD file specified.",
223 if (!($mode =~ /^(clib|dump|fd|libproto|lvo|functable|macros|proto|pragmas|stubs|gateproto|gatestubs|sdiproto|sdistubs|verify)$/)) {
224 pod2usage
(-message
=> "Unknown mode specified. Use --help for a list.",
229 if ($libarg !~ /^(first|last|none)$/) {
230 pod2usage
(-message
=> "Unknown libarg specified. Use --help for a list.",
235 if ($addvectors !~ /^(none|library|device|boopsi)$/) {
236 pod2usage
(-message
=> "Unknown addvectors value. Use --help for a list.",
242 foreach my $target_regex (keys %targets) {
243 if ($target =~ /^$target_regex$/) {
244 $classes = $targets{$target_regex};
249 pod2usage
(-message
=> "Unknown target specified. Use --help for a list.",
256 open( OLDOUT
, ">&STDOUT" );
258 for my $i ( 0 .. $#ARGV ) {
259 my $sfd = parse_sfd
($ARGV[$i]);
260 my $num = $#{$$sfd{'prototypes'}};
266 $obj = CLib
->new( sfd
=> $sfd );
271 $obj = FD
->new( sfd
=> $sfd );
276 $obj = Dump
->new( sfd
=> $sfd );
281 $obj = Gate
->new( sfd
=> $sfd,
288 $obj = LVO
->new( sfd
=> $sfd );
292 /^functable$/ && do {
293 $obj = FuncTable
->new( sfd
=> $sfd );
298 $obj = $$classes{'macros'}->new( sfd
=> $sfd );
300 # By tradition, the functions in the macro files are sorted
301 # @{$$sfd{'prototypes'}} = sort {
302 # $$a{'funcname'} cmp $$b{'funcname'}
303 # } @{$$sfd{'prototypes'}};
308 $obj = Proto
->new( sfd
=> $sfd );
313 $obj = SASPragmas
->new( sfd
=> $sfd );
318 $obj = Verify
->new( sfd
=> $sfd );
323 $obj = $$classes{'stubs'}->new( sfd
=> $sfd );
325 # By tradition, the functions in the stub files are sorted
326 # @{$$sfd{'prototypes'}} = sort {
327 # $$a{'funcname'} cmp $$b{'funcname'}
328 # } @{$$sfd{'prototypes'}};
332 /^gateproto$/ && do {
333 $obj = $$classes{'gatestubs'}->new( sfd
=> $sfd,
339 /^gatestubs$/ && do {
340 $obj = $$classes{'gatestubs'}->new( sfd
=> $sfd,
348 $obj = $$classes{'sdistubs'}->new( sfd
=> $sfd,
355 $obj = $$classes{'sdistubs'}->new( sfd
=> $sfd,
361 die "Unknown mode specified: " . $mode;
365 for my $j ( 0 .. $num + 1) {
366 my $prototype = $$sfd{'prototypes'}[$j];
367 my $funcname = $$prototype{'funcname'};
369 if (!defined ($funcname) || will_close_output
($sfd, $funcname) != 0) {
377 if (open_output
($sfd, $funcname) != 0) {
381 $obj->function (prototype => $prototype);
388 print STDERR
"All done.\n";
391 open (STDOUT
, ">&OLDOUT");
401 ###############################################################################
402 ### Subroutines ###############################################################
403 ###############################################################################
406 ### parse_sfd: Parse an SFD file hand return a hash record #####################
408 sub parse_sfd
( $ ) {
412 my $type = 'function';
413 my $last_type = $type;
420 copyright
=> 'Copyright © 2001 Amiga, Inc.',
424 basetype
=> 'struct Library *',
433 # Why do I need this????
434 $$result{'prototypes'} = ();
435 $$result{'includes'} = ();
436 $$result{'typedefs'} = ();
438 if ($addvectors ne 'none') {
439 push @
{$$result{'includes'}}, '<dos/dos.h>';
440 push @
{$$result{'includes'}}, '<exec/execbase.h>';
442 if ($addvectors eq 'device') {
443 push @
{$$result{'includes'}}, '<exec/io.h>';
445 elsif ($addvectors eq 'boopsi') {
446 push @
{$$result{'includes'}}, '<intuition/classes.h>';
449 for my $i ( 0 .. $#{$classes->{vectors}->{$addvectors}} ) {
450 push @
{$$result{'prototypes'}}, {
452 subtype
=> $addvectors,
453 value
=> $classes->{vectors
}->{$addvectors}[$i],
468 ( my $fn = $file ) =~ s
,.*[/\\](.*),$1,;
469 print STDERR
"Processing SFD file '$fn'.\n";
473 unless (open (SFD
, "<" . $file)) {
474 print STDERR
"Unable to open file '$file'.\n";
481 while (my $line = <SFD
>) {
486 /^==copyright\s/ && do {
487 ( $$result{'copyright'} = $_ ) =~ s/==copyright\s+(.*)\s*/$1/;
492 ( $$result{'id'} = $_ ) =~ s/==id\s+(.*)\s*/$1/;
496 /^==libname\s+/ && do {
497 ( $$result{'libname'} = $_ ) =~ s/==libname\s+(.*)\s*/$1/;
502 ( $$result{'base'} = $_ ) =~ s/==base\s+_?(.*)\s*/$1/;
506 /^==basetype\s+/ && do {
507 ( $$result{'basetype'} = $_ ) =~ s/==basetype\s+(.*)\s*/$1/;
511 /^==include\s+/ && do {
512 ( my $inc = $_ ) =~ s/==include\s+(.*)\s*/$1/;
514 push @
{$$result{'includes'}}, $inc;
518 /^==typedef\s+/ && do {
519 ( my $td = $_ ) =~ s/==typedef\s+(.*)\s*$/$1/;
521 push @
{$$result{'typedefs'}}, $td;
526 ( $bias = $_ ) =~ s/==bias\s+(.*)\s*/$1/;
530 /^==reserve\s+/ && do {
531 ( my $reserve = $_ ) =~ s/==reserve\s+(.*)\s*/$1/;
533 $bias += 6 * $reserve;
537 /^==alias\s*$/ && do {
544 /^==varargs\s*$/ && do {
551 /^==private\s*$/ && do {
556 /^==public\s*$/ && do {
561 /^==version\s+/ && do {
562 ( $version = $_ ) =~ s/==version\s+(.*)\s*/$1/;
571 ( my $cmt = $_ ) =~ s/^\*(.*)\s*/$1/;
573 $comment .= ($comment eq '' ?
"" : "\n" ) . $cmt;
578 # Strip whitespaces and append
579 $line =~ s/\s*(.*)\s*/$1/;
580 $proto_line .= $line . " ";
589 # If we get here, we found a line we don't understand
590 print STDERR
"Unable to parse line $line_no in SFD file" .
591 " '$file'. The line looks like this:\n" . $line ;
596 /.*[A-Za-z0-9_]+\s*\(.*\).*\(((base|sysv|autoreg|[\saAdD][0-7]-?),?)*\)\s*$/
599 if ($proto_line =~ /.*\(.*[0-7]-.*\)\s*$/) {
600 if ($$classes{'target'} ne 'amigaos') {
601 print STDERR
"Warning: Multiregister functions are m68k only.\n";
603 $proto_line =~ s/([da][0-7])-[da][0-7]/$1/g;
606 push @
{$$result{'prototypes'}}, {
609 value
=> $proto_line,
627 if( $proto_line ne '' ) {
628 # If $proto_line isn't empty, we couldn't parse it
629 die "Unhanled proto '" . $proto_line . "'\n";
634 # Now parse the prototypes
635 my $real_funcname = '';
636 my $real_prototype = {};
637 my $varargs_type = '';
639 for my $i ( 0 .. $#{$$result{'prototypes'}} ) {
640 my $prototype = $$result{'prototypes'}[$i];
642 if ($$prototype{'type'} eq 'varargs') {
643 $$prototype{'real_funcname'} = $real_funcname;
644 $$prototype{'real_prototype'} = $real_prototype;
647 $$prototype{'real_funcname'} = '';
648 $$prototype{'real_prototype'} = '';
651 parse_proto
($result, $prototype, $varargs_type);
653 if ($$prototype{'type'} eq 'function') {
654 $varargs_type = $$prototype{'argtypes'}[$#{$$prototype{'argtypes'}}];
657 if ($$prototype{'type'} eq 'function') {
658 $real_funcname = $$prototype{'funcname'};
659 $real_prototype = $prototype;
663 # Create some other variables
665 ( $$result{'basename'} = $file ) =~ s
:.*/(\w
+?
)_lib\
.sfd
:$1:;
667 if ($$result{'basename'} eq '') {
668 ( $$result{'basename'} = $$result{'libname'} ) =~ s/(.*)\.\w+/$1/ or do {
669 print STDERR
"Unable to find or guess base name.\n";
670 print STDERR
"Please add \"==libname module_name\" to SFD file.\n";
674 # Fake the CIA libname
675 if ($$result{'basename'} eq "cia") {
676 $$result{'libname'} = "ciaX.resource";
679 $$result{'libname'} = $$result{'basename'} . ".library";
683 # Fake the Workbench basename
684 if ($$result{'basename'} eq "workbench") {
685 $$result{'basename'} = "wb";
688 $$result{'basename'} =~ s/-/_/g;
689 $$result{'basename'} =~ s/\//_
/g
;
690 $$result{'basename'} =~ s/\./_/g;
691 $$result{'basename'} =~ s/@/_/g;
692 $$result{'basename'} = lc $$result{'basename'};
693 $$result{'BASENAME'} = uc $$result{'basename'};
694 $$result{'Basename'} = ucfirst $$result{'basename'};
695 ($result->{BaseName
} = $result->{base
}) =~ s/Base//;
701 ### parse_proto: Parse a single function prototype ###########################
703 sub parse_proto
( $$$ ) {
705 my $prototype = shift;
706 my $varargs_type = shift;
713 if (!(($return,undef,undef,$name,$arguments,$registers) =
714 ( $$prototype{'value'} =~
715 /^((struct\s+)?(\w+\s*?)+\**)\s*(\w+)\s*\((.*)\)\s*\((.*)\).*/ ))) {
716 print STDERR
"Unable to parse prototype on line $$prototype{'line'}.\n";
720 # Nuke whitespaces from the register specification
721 $registers =~ s/\s//;
723 $$prototype{'return'} = $return;
724 $$prototype{'funcname'} = $name;
726 $$prototype{'numargs'} = 0;
727 $$prototype{'numregs'} = 0;
729 @
{$$prototype{'regs'}} = ();
730 @
{$$prototype{'args'}} = ();
731 @
{$$prototype{'___args'}} = ();
732 @
{$$prototype{'argnames'}} = ();
733 @
{$$prototype{'___argnames'}} = ();
734 @
{$$prototype{'argtypes'}} = ();
736 if ($arguments =~ /^(void|VOID)$/) {
740 my @args = split(/,/,$arguments);
742 # Fix function pointer arguments and build $$prototype{'args'}
745 foreach my $arg (@args) {
747 $arg =~ s/\s*(.*?)\s*/$1/;
750 my $old_arg = pop @
{$$prototype{'args'}};
752 push @
{$$prototype{'args'}}, $old_arg . "," . $arg;
755 push @
{$$prototype{'args'}}, $arg;
758 # Count parentheses (a function pointer arguments is processed
759 # when $par_cnt is 0).
760 $par_cnt += ( $arg =~ tr/\(/\(/ );
761 $par_cnt -= ( $arg =~ tr/\)/\)/ );
764 $$prototype{'numargs'} = $#{$$prototype{'args'}} + 1;
766 if ($registers =~ /sysv/) {
767 $prototype->{type
} = 'cfunction';
768 $prototype->{nb
} = 1;
770 elsif ($registers =~ /autoreg/) {
773 foreach my $arg (@
{$$prototype{'args'}}) {
775 push @
{$$prototype{'regs'}}, "a$a_cnt";
779 push @
{$$prototype{'regs'}}, "d$d_cnt";
784 $prototype->{numregs
} = $#{$$prototype{'regs'}} + 1;
785 $prototype->{nb
} = $sfd->{base
} eq '';
788 # Split regs and make them lower case
789 @
{$$prototype{'regs'}} = split(/,/,lc $registers);
790 $prototype->{numregs
} = $#{$$prototype{'regs'}} + 1;
791 $prototype->{nb
} = $sfd->{base
} eq '' || $registers =~ /a6/;
794 $$prototype{'nr'} = $$prototype{'return'} =~ /^(VOID|void)$/;
797 # printfcall: LONG Printf( STRPTR format, ... );
798 # All varargs are optional
799 # tagcall: BOOL AslRequestTags( APTR requester, Tag Tag1, ... );
800 # First vararg is a Tag, then a TAG_DONE terminated tag list
801 # methodcall: ULONG DoGadgetMethod( ... ULONG message, ...);
802 # First vararg is required.
804 if ($prototype->{type
} eq 'varargs') {
806 /^\s*(const|CONST)?\s*struct\s+TagItem\s*\*\s*$/ ) {
807 $prototype->{subtype
} = 'tagcall';
809 if ($prototype->{numargs
} == $prototype->{numregs
}) {
811 print STDERR
"Warning: Adding missing Tag argument to " .
812 $prototype->{funcname
} . "()\n";
815 my $last = pop @
{$prototype->{args
}};
816 push @
{$prototype->{args
}}, "Tag _tag1" ;
817 push @
{$prototype->{args
}}, $last;
819 ++$prototype->{numargs
};
823 if ($prototype->{numargs
} == $prototype->{numregs
}) {
824 $prototype->{subtype
} = 'printfcall';
826 elsif ($prototype->{numargs
} == $prototype->{numregs
} + 1) {
827 $prototype->{subtype
} = 'methodcall';
831 elsif ($prototype->{type
} eq 'cfunction') {
832 foreach (split(/,/,lc $registers)) {
834 $prototype->{subtype
} = 'sysv';
839 if ($sfd->{base
} eq '') {
840 printf STDERR
"$prototype->{funcname}: " .
841 "Library has no base!\n";
845 $prototype->{nb
} = 0;
853 # Make sure we have the same number of arguments as registers, or,
854 # if this is a varargs function, possible one extra, á la "MethodID, ...".
855 # Tagcalls always have one extra, á la "Tag, ...".
857 if (($prototype->{type
} eq 'varargs' &&
858 $prototype->{subtype
} eq 'tagcall' &&
859 $prototype->{numargs
} != $prototype->{numregs
} + 1 ) ||
861 ($prototype->{type
} eq 'varargs' &&
862 $prototype->{subtype
} eq 'printfcall' &&
863 $prototype->{numargs
} != $prototype->{numregs
}) ||
865 ($prototype->{type
} eq 'varargs' &&
866 $prototype->{subtype
} eq 'methodcall' &&
867 $prototype->{numargs
} != $prototype->{numregs
} + 1) ||
869 ($prototype->{type
} eq 'function' &&
870 $prototype->{numargs
} != $prototype->{numregs
})) {
872 print STDERR
"Failed to parse arguments/registers on SFD " .
873 "line $$prototype{'line'}:\n$$prototype{'value'}\n";
874 print STDERR
"The number of arguments doesn't match " .
875 "the number of registers (+1 if tagcall).\n";
881 foreach my $arg (@
{$$prototype{'args'}}) {
886 # MorhOS includes use __CLIB_PROTOTYPE for some reason ...
887 if ($arg =~ /.*\(.*?\)\s*(__CLIB_PROTOTYPE)?\(.*\)/) {
891 ($type1, $name, $type2) =
892 ( $arg =~ /^\s*(.*)\(\s*\*\s*(\w+)\s*\)\s*(\w*\(.*\))\s*/ );
893 $type = "$type1(*)$type2";
894 $___name = "___$name";
895 $___arg = "$type1(*___$name) $type2";
897 elsif ($arg !~ /^\.\.\.$/) {
898 ($type, $name) = ( $arg =~ /^\s*(.*?[\s*]*?)\s*(\w+)\s*$/ );
899 $___name = "___$name";
900 $___arg = "$type ___$name";
903 if ($prototype->{type
} eq 'varargs') {
904 $type = $varargs_type;
916 if ($type eq '' || $name eq '' ) {
917 print STDERR
"Type or name missing from '$arg'.\n";
921 push @
{$$prototype{'___args'}}, $___arg;
922 push @
{$$prototype{'argnames'}}, $name;
923 push @
{$$prototype{'___argnames'}}, $___name;
925 push @
{$$prototype{'argtypes'}}, $type;
935 ### close_output: Close the output file if necessary #########################
937 sub close_output
() {
943 ### check_output: Check if the file will be reopended by open_output ##########
945 sub will_close_output
( $$ ) {
947 my $function = shift;
949 my $new_output = $output;
951 $new_output =~ s/%f/$function/;
952 $new_output =~ s/%b/$$sfd{'base'}/;
953 $new_output =~ s/%l/$$sfd{'libname'}/;
954 $new_output =~ s/%n/$$sfd{'basename'}/;
956 if( $old_output ne '' &&
957 $new_output ne $old_output ) {
965 ### open_output: (Re)open the output file if necessary #######################
967 sub open_output
( $$ ) {
969 my $function = shift;
971 my $new_output = $output;
973 $new_output =~ s/%f/$function/;
974 $new_output =~ s/%b/$$sfd{'base'}/;
975 $new_output =~ s/%l/$$sfd{'libname'}/;
976 $new_output =~ s/%n/$$sfd{'basename'}/;
978 if( $new_output ne $old_output ) {
982 if ($new_output eq '-') {
983 open (STDOUT
, ">&OLDOUT") or die;
986 open (STDOUT
, ">" . $new_output) or die;
989 print STDERR
"Writing to '$new_output'\n";
993 $old_output = $new_output;