diff options
-rw-r--r-- | sys/kern/makedevops.pl | 527 |
1 files changed, 350 insertions, 177 deletions
diff --git a/sys/kern/makedevops.pl b/sys/kern/makedevops.pl index 6e473be..24e0b14 100644 --- a/sys/kern/makedevops.pl +++ b/sys/kern/makedevops.pl @@ -1,4 +1,4 @@ -#!/usr/bin/perl -w +#!/usr/bin/perl # # Copyright (c) 1992, 1993 # The Regents of the University of California. All rights reserved. @@ -31,191 +31,364 @@ # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # -# From @(#)vnode_if.sh 8.1 (Berkeley) 6/10/93 -# From Id: makedevops.sh,v 1.1 1998/06/14 13:53:12 dfr Exp -# $Id$ +# From @(#)vnode_if.sh 8.1 (Berkeley) 6/10/93 +# From @(#)makedevops.sh 1.1 1998/06/14 13:53:12 dfr Exp $ +# From @(#)makedevops.sh ?.? 1998/10/05 + +# +# Script to produce device front-end sugar. # -use strict; -use IO::Handle; -use IO::File; -use Getopt::Std; - -sub main { - my (%opts) = ('c' => 0, 'h' => 0); - my ($src, $cname, $hname, $tmp, $intname); - - getopts('ch', \%opts); - if (!($opts{'c'} or $opts{'h'}) or $#ARGV != 0) { - &usage(); - } - - $cname = $ARGV[0]; - $cname =~ s,^.*/([^/]+)$,$1,; - $hname = $cname; - $cname =~ s/\.m$/.c/; - $hname =~ s/\.m$/.h/; - - $src = new IO::File "<$ARGV[0]"; - defined($src) - or die "$0: $ARGV[0]: $!\n"; - - $tmp = IO::File->new_tmpfile(); - defined($tmp) - or die "$0: creating temporary file: $!\n"; - - write_header($tmp, $ARGV[0]); - if ($opts{'c'}) { - print $tmp ("#include <sys/param.h>\n", - "#include <sys/queue.h>\n", - "#include <sys/bus_private.h>\n"); - } - -line: - while (<$src>) { - chomp; - - if (/^\#\s*(if)|(else)|(elif)|(endif)|(include)/) { - if ($opts{'c'}) { - print $tmp ($_, "\n"); - } - next line; - } - - s/\#.*$//; # strip comments - next line if (/^\s*$/); - - if (/^\s*INTERFACE\s+(\w+)\s*$/) { - $intname = $1; - if ($opts{'c'}) { - print $tmp "#include \"$hname\"\n\n"; - } else { - print $tmp ("#ifndef _", $intname, "_if_h_\n", - "#define _", $intname, "_if_h_\n\n"); - } - next line; - } - if (/^\s*METHOD\s+([a-zA-Z_0-9 *]+)\s+(\w+)\s*\{/) { - my ($ret, $name) = ($1, $2); - my (@args, $mname, $umname); - - # Get the function arguments. - @args = (); -arg: - while (<$src>) { - chomp; - s/\#.*$//; # delete comments - last arg if (/^\s*\}\s*;/); - s/^\s+//; # strip leading whitespace - s/\s+$//; # strip trailing whitespace - s/;$//; # strip trailing semicolon - s/\s+/ /g; # squish internal whitespace to a single space - push(@args, $_); - } - - $mname = $intname . '_' . $name; - $umname = uc $mname; - - # Print out the method declaration - if ($opts{'h'}) { - print $tmp ("extern struct device_op_desc ", $mname, - "_desc;\n", - $ret, ' ', $umname, "(", - join(", ", @args), - ");\n"); - } - - if ($opts{'c'}) { - # Print the method desc - print $tmp ("struct device_op_desc ", $mname, "_desc = {\n", - "\t0,\n", - "\t\"$mname\"\n", - "};\n\n"); - - # Print out the method typedef - print $tmp ("typedef ", $ret, ' ', $mname, "_t (", - join(", ", @args), ");\n"); - - # Print out the method itself - print $tmp ($ret, ' ', $umname, " (", - join(", ", @args), ")\n", - "{\n", - "\t", $mname, "_t *m = (", $mname, "_t *)", - "DEVOPMETH(dev, ", $mname, ");\n"); - if ($ret eq 'void') { - print $tmp "\tm("; - } else { - print $tmp "\treturn m("; - } - print $tmp join(", ", map {&argname($_)} @args); - print $tmp ");\n}\n\n"; - } - next line; - } - # should diagnose unrecognized input here - } - - if ($opts{'h'}) { - print $tmp ("\n#endif /* _", $intname, "_if_h_ */\n"); - } - - compare_and_update($tmp, $opts{'c'} ? $cname : $hname); - exit 0; -} +$debug = 0; +$cfile = 0; # by default do not produce any file type +$hfile = 0; -&main; +$keepcurrentdir = 1; -sub argname { - my ($arg) = @_; - my (@words) = split(/\s+/, $arg); - my ($name) = pop @words; - - $name =~ s/^\*+//; - return $name; -} +$line_width = 80; + +use File::Basename; -sub compare_and_update { - my ($oldfh, $newname) = @_; - my ($data1, $data2, $newfh); - - defined($oldfh->seek(0, 0)) - or die "$0: seek: $!\n"; - $oldfh->input_record_separator(undef); - $data1 = <$oldfh>; - - $newfh = new IO::File "<$newname"; - if (defined($newfh)) { - $newfh->input_record_separator(undef); - $data2 = <$newfh>; - undef $newfh; - } - if (defined($data2) && $data1 eq $data2) { - printf STDERR "$0: $newname: unchanged\n"; - return 0; - } - - $newfh = new IO::File ">$newname"; - die "$0: $newname: $!\n" - unless(defined $newfh); - print $newfh $data1; - return 0; +# Process the command line +# +while ( $arg = shift @ARGV ) { + if ( $arg eq '-c' ) { + warn "Producing .c output files" + if $debug; + $cfile = 1; + } elsif ( $arg eq '-h' ) { + warn "Producing .h output files" + if $debug; + $hfile = 1; + } elsif ( $arg eq '-ch' || $arg eq '-hc' ) { + warn "Producing .c and .h output files" + if $debug; + $cfile = 1; + $hfile = 1; + } elsif ( $arg eq '-d' ) { + $debug = 1; + } elsif ( $arg eq '-p' ) { + warn "Will produce files in original not in current directory" + if $debug; + $keepcurrentdir = 0; + } elsif ( $arg eq '-l' ) { + if ( $line_width = shift @ARGV and $line_width > 0 ) { + warn "Line width set to $line_width" + if $debug; + } else { + die "Please specify a valid line width after -l"; + } + } elsif ( $arg =~ m/\.m$/ ) { + warn "Filename: $arg" + if $debug; + push @filenames, $arg; + } else { + warn "$arg ignored" + if $debug; + } } -sub write_header { - my ($out, $in) = @_; - print $out <<EOH; -/* - * This file is produced automatically. - * Do not modify anything in here by hand. - * - * Created from $in with makedevops.pl - */ +# Validate the command line parameters +# +die "usage: $0 [-d] [-p] [-c|-h] srcfile +where -c produce only .c files + -h produce only .h files + -p use the path component in the source file for destination dir + -l set line width for output files [80] + -d switch on debugging +" + unless ($cfile or $hfile) + and $#filenames != -1; + +# FIXME should be able to do this more easily +# +$tmpdir = $ENV{'TMPDIR'}; # environment variables +$tmpdir = $ENV{'TMP'} + if !$tmpdir; +$tmpdir = $ENV{'TEMP'} + if !$tmpdir; +$tmpdir = '/tmp' # look for a physical directory + if !$tmpdir and -d '/tmp'; +$tmpdir = '/usr/tmp' + if !$tmpdir and -d '/usr/tmp'; +$tmpdir = '/var/tmp' + if !$tmpdir and -d '/var/tmp'; +$tmpdir = '.' # give up and use current dir + if !$tmpdir; + +foreach $src ( @filenames ) { + # Names of the created files + $ctmpname = "$tmpdir/ctmp.$$"; + $htmpname = "$tmpdir/htmp.$$"; + + ($name, $path, $suffix) = &fileparse($src, '.m'); + $path = '.' + if $keepcurrentdir; + $cfilename="$path/$name.c"; + $hfilename="$path/$name.h"; + + warn "Processing from $src to $cfile / $hfile via $ctmp / $htmp" + if $debug; + + die "Could not open $src, $!" + if !open SRC, "$src"; + die "Could not open $ctmpname, $!" + if $cfile and !open CFILE, ">$ctmpname"; + die "Could not open $htmpname, $!" + if $hfile and !open HFILE, ">$htmpname"; + + if ( $cfile ) { + # Produce the header of the C file + # + print CFILE "/*\n"; + print CFILE " * This file is produced automatically.\n"; + print CFILE " * Do not modify anything in here by hand.\n"; + print CFILE " *\n"; + print CFILE " * Created from\n"; + print CFILE " * $src\n"; + print CFILE " * with\n"; + print CFILE " * $0\n"; + print CFILE " */\n"; + print CFILE "\n"; + print CFILE "#include <sys/param.h>\n"; + print CFILE "#include <sys/queue.h>\n"; + print CFILE "#include <sys/bus_private.h>\n"; + } + + if ( $hfile ) { + # Produce the header of the H file + # + print HFILE "/*\n"; + print HFILE " * This file is produced automatically.\n"; + print HFILE " * Do not modify anything in here by hand.\n"; + print HFILE " *\n"; + print HFILE " * Created from\n"; + print HFILE " * $src\n"; + print HFILE " * with\n"; + print HFILE " * $0\n"; + print HFILE " */\n"; + print HFILE "\n"; + } + + %methods = (); # clear list of methods + $lineno = 0; + $error = 0; # to signal clean up and gerror setting + + LINE: while ( $line = <SRC> ) { + $lineno++; + + # take special notice of include directives. + # + if ( $line =~ m/^#\s*include\s+(["<])([^">]+)([">]).*/i ) { + warn "Included file: $1$2" . ($1 eq '<'? '>':'"') + if $debug; + print CFILE "#include $1$2" . ($1 eq '<'? '>':'"') . "\n" + if $cfile; + } + + $line =~ s/#.*//; # remove comments + $line =~ s/^\s+//; # remove leading ... + $line =~ s/\s+$//; # remove trailing whitespace + + if ( $line =~ m/^$/ ) { # skip empty lines + # nop + + } elsif ( $line =~ m/^INTERFACE\s*([^\s;]*)(\s*;?)/i ) { + $intname = $1; + $semicolon = $2; + unless ( $intname =~ m/^[a-z_][a-z0-9_]*$/ ) { + warn $line + if $debug; + warn "$src:$lineno: Invalid interface name '$intname', use [a-z_][a-z0-9_]*"; + $error = 1; + last LINE; + } + + warn "$src:$lineno: semicolon missing at end of line, no problem" + if $semicolon !~ s/;$//; + + warn "Interface $intname" + if $debug; + + print HFILE '#ifndef _'.$intname."_if_h_\n" + if $hfile; + print HFILE '#define _'.$intname."_if_h_\n\n" + if $hfile; + print CFILE '#include "'.$intname.'_if.h"'."\n\n" + if $cfile; -EOH + } elsif ( $line =~ m/^METHOD/i ) { + # Get the return type function name and delete that from + # the line. What is left is the possibly first function argument + # if it is on the same line. + # + # FIXME For compatibilities sake METHOD and METHODE is accepted. + # + if ( !$intname ) { + warn "$src:$lineno: No interface name defined"; + $error = 1; + last LINE; + } + $line =~ s/^METHODE?\s+([^{]+?)\s*{\s*//i; + @ret = split m/\s+/, $1; + $name = pop @ret; # last element is name of method + $ret = join(" ", @ret); # return type + + warn "Method: name=$name return type=$ret" + if $debug; + + if ( !$name or !$ret ) { + warn $line + if $debug; + warn "$src:$lineno: Invalid method specification"; + $error = 1; + last LINE; + } + + unless ( $name =~ m/^[a-z_][a-z_0-9]*$/ ) { + warn $line + if $debug; + warn "$src:$lineno: Invalid method name '$name', use [a-z_][a-z0-9_]*"; + $error = 1; + last LINE; + } + + if ( defined($methods{$name}) ) { + warn "$src:$lineno: Duplicate method name"; + $error = 1; + last LINE; + } + + $methods{$name} = 'VIS'; + + while ( $line !~ m/}/ and $line .= <SRC> ) { } + + if ( $line !~ s/};?(.*)// ) { # remove first '}' and trailing garbage + # The '}' was not there (the rest is optional), so complain + warn "$src:$lineno: Premature end of file"; + $error = 1; + last LINE; + } + warn "$src:$lineno: Ignored '$1'" # warn about garbage at end of line + if $debug and $1; + + # Create a list of variables without the types prepended + # + $line =~ s/^\s+//; # remove leading ... + $line =~ s/\s+$//; # ... and trailing whitespace + $line =~ s/\s+/ /; # remove double spaces + + @arguments = split m/\s*;\s*/, $line; + @varnames = (); # list of varnames + foreach $argument (@arguments) { + next # skip argument if argument is empty + if !$argument; + + @ar = split m/[*\s]+/, $argument; + if ( $#ar == 0 ) { # only 1 word in argument? + warn "$src:$lineno: no type for '$argument'"; + $error = 1; + last LINE; + } + + push @varnames, $ar[-1]; # last element is name of variable + }; + + warn 'Arguments: ' . join(', ', @arguments) . "\n" + . 'Varnames: ' . join(', ', @varnames) + if $debug; + + $mname = $intname.'_'.$name; # method name + $umname = uc($mname); # uppercase method name + + $arguments = join(", ", @arguments); + $varnames = join(", ", @varnames); + + if ( $hfile ) { + # the method description + print HFILE "extern struct device_op_desc $mname\_desc;\n"; + # the method typedef + print HFILE &format_line("typedef $ret $mname\_t($arguments);", + $line_width, ', ', + ',',' ' x length("typedef $ret $mname\_t(")) + . "\n"; + # the method declaration + print HFILE "$mname\_t $umname;\n\n"; + } + + if ( $cfile ) { + # Print out the method desc + print CFILE "struct device_op_desc $mname\_desc = {\n"; + print CFILE "\t0, \"$mname\"\n"; + print CFILE "};\n\n"; + + # Print out the method itself + if ( 0 ) { # haven't chosen the format yet + print CFILE "$ret $umname($varnames)\n"; + print CFILE "\t".join(";\n\t", @arguments).";\n"; + } else { + print CFILE &format_line("$ret $umname($arguments)", + $line_width, ', ', + ',', ' ' x length("$ret $umname(")) . "\n"; + } + print CFILE "{\n"; + print CFILE &format_line("\t$mname\_t *m = ($mname\_t *) DEVOPMETH(dev, $mname);", + $line_width-8, ' = ', ' =', "\t\t") + . "\n"; + print CFILE "\t".($ret eq 'void'? '':'return ') . "m($varnames);\n"; + print CFILE "}\n\n"; + } + } else { + warn $line + if $debug; + warn "$src:$lineno: Invalid line encountered"; + $error = 1; + last LINE; + } + } # end LINE + + # print the final '#endif' in the header file + # + print HFILE "#endif /* _".$intname."_if_h_ */\n" + if $hfile; + + close SRC; + close CFILE + if $cfile; + close HFILE + if $hfile; + + if ( !$error ) { + if ( $cfile ) { + ($rc = system("mv $ctmpname $cfilename")) + and warn "mv $ctmpname $cfilename failed, $rc"; + } + + if ( $hfile ) { + ($rc = system("mv $htmpname $hfilename")) + and warn "mv $htmpname $hfilename failed, $rc"; + } + } else { + warn 'File' . ($hfile and $cfile? 's':'') . ' skipped'; + ($rc = system("rm -f $htmpname $ctmpname")) + and warn "rm -f $htmpname $ctmpname failed, $rc"; + $gerror = 1; + } } -sub usage { - print STDERR "$0: usage:\n\t$0 -c infile\n\t$0 -h infile\n"; - exit 1; +exit $gerror; + + +sub format_line { + my ($line, $maxlength, $break, $new_end, $new_start) = @_; + my $rline = ""; + + while ( length($line) > $maxlength + and ($i = rindex $line, $break, $maxlength-length($new_end)) != -1 ) { + $rline .= substr($line, 0, $i) . $new_end . "\n"; + $line = $new_start . substr($line, $i+length($break)); + } + + return $rline . $line; } |