diff options
author | agg1 <agg1@ae88bc3d-4319-0410-8dbf-d08b4c9d3795> | 2000-07-25 01:13:53 +0000 |
---|---|---|
committer | agg1 <agg1@ae88bc3d-4319-0410-8dbf-d08b4c9d3795> | 2000-07-25 01:13:53 +0000 |
commit | fd580588f1899b2a824790a9ab7805805c981595 (patch) | |
tree | aeb36d1ad6e9a2372146cc97f10f7324aa5cb60b /bin | |
parent | 416e1bfb2c009a2d6100b0aa518afa10113f71b6 (diff) | |
download | ATCD-fd580588f1899b2a824790a9ab7805805c981595.tar.gz |
*** empty log message ***
Diffstat (limited to 'bin')
-rwxr-xr-x | bin/split-cpp | 422 |
1 files changed, 422 insertions, 0 deletions
diff --git a/bin/split-cpp b/bin/split-cpp new file mode 100755 index 00000000000..513c52a946c --- /dev/null +++ b/bin/split-cpp @@ -0,0 +1,422 @@ +eval '(exit $?0)' && eval 'exec perl -w -S $0 ${1+"$@"}' + & eval 'exec perl -w -S $0 $argv:q' + if 0; + +# Splits C++ source files into one file per function or data item. +# +# Author: David L. Levine, with much help and encouragment from +# Umar Syyid and Gonzalo A. Diethelm. +# Completed by Andrew Gilpin, July 2000 +# Date: 10 November 1998 +# +# For each C++ source file: +# 1) Extracts the "intro" code, i.e., #includes and declarations. +# 2) Identifies function definitions, relying on {, and } at the +# beginning of a line, to delineate the function begin and +# end. +# +# Assumptions: (applies only to the files being split, i.e. .cpp files) +# * Function definition bodies are terminated with } appearing at +# the beginning of a line. +# * Free-standing (outside of functions) macro invocations must be +# followed by a blank line, or terminated with a semicolon. +# * A function must not have a blank line between its header +# (signature) and its body. +# * There aren't multiple C-style comments on one line, with code +# between them. +# * typedefs are on a single line +# * A #endif doesn't have a multi-line C comment starting on that line. + +# The first three lines above let this script run without specifying the +# full path to perl, as long as it is in the user's PATH. +# Taken from perlrun man page. + +# Changes made by Andrew Gilpin (June - July 2000) +# * Added option -c to use .c extension instead of .cpp extension +# * Prints message when no filenames are specified on the command line +# * Changed -? option to -h so that it works properly in most shells +# * Added option -s to skip certain files, but copy them to $split_dir, +# renaming them. (filename.cpp -> $split_dir/filename_S1.cpp). This is +# here so that ACE can selectively not split certain files (namely those +# that this script doesn't work with :) +# * Added support for classes declared in the .cpp file. + +$usage="usage: $0 [-h] [-d] [-v] [-c] [-s filename] filenames\n"; + +#### Configuration parameters. +$verbose = 0; +$debug = 0; +$split_dir = 'SPLIT'; +$extension = 'cpp'; +@files_to_skip = (); + +#### Constants. +$DIR_SEPARATOR = $^O eq "MSWin32" ? '\\' : '/'; + + +#### +#### Process command line args. +#### +while ( $#ARGV >= $[ && $ARGV[0] =~ /^-/ ) { + if ( $ARGV[0] eq '-d' ) { + $debug = 1; + } elsif ( $ARGV[0] eq '-v' ) { + $verbose = 1; + } elsif ( $ARGV[0] eq '-c' ) { + $extension = 'c'; + } elsif ( $ARGV[0] eq '-s' ) { + push @files_to_skip, $ARGV[1]; + shift; + } elsif ( $ARGV[0] eq '-h' ) { + print "$usage"; + exit; + } else { + print STDERR "$0: unknown option $ARGV[0]\n"; + die $usage; + } + shift; +} + + +&main (); + + +#### +#### Reset state, to process a new file starting with a clean slate. +#### +sub reset { + #### Working data buffers. + @intro = (); + @current_comments = (); + @current_code = (); + @if = (); + @save_if = (); + @endif = (); + @unknown = (); + ####@unknown_s = (); + + #### State variables. + $current_file_number = 0; + $top_of_file = 1; + $in_braces = 0; + $in_nonfunction_code = 0; + $in_C_comment = 0; + $intro_length = 0; + $preprocessor_continuation = 0; + $preserved_ifs = 0; +} + + +sub main { + #### Print error message if no files are specified. + #### We need to do this before we modify anything on disk. + die "No files specified!\n$usage" if (@ARGV == 0); + + #### Remove the destination subdirectory, if it exists. + #### Attempts to clean it out using unlink may fail because + #### it can have many files. + if (-d "$split_dir") { + system ("/bin/rm -r $split_dir") << 256 && + die "$0: unable to rm \"$split_dir\"\n"; + } + + #### Create the destination subdirectory. + mkdir "$split_dir", 0755 || + die "$0: unable to create $split_dir directory: $!\n"; + + MAIN_LOOP: foreach $file (@ARGV) { + #### Strip off filename extension. + ($basename = $file) =~ s/\.[^\.]+$//; + + foreach $skip_file (@files_to_skip) { + if ($skip_file eq $file) { + system ("/bin/cp $file $split_dir/" . $basename. "_S1\.$extension"); + next MAIN_LOOP; + } + } + + &reset (); + + print "FILE: $file\n" if $verbose; + open INPUT, "$file" || die "$0: unable to open \"$file\"\n"; + + while (<INPUT>) { + #### Strip comments from $line and use that for processing. + #### But, use $_ for output, so that comments will be preserved. + my $line = $_; + + #### If we're in the midst of a multiline C comment, see + #### if it's finished on this line. + if ($in_C_comment) { + if ($line =~ s%^.*\*/%%) { + #### End C-style comment. + $in_C_comment = 0; + + if ($line =~ /^\s*$/ && ! $in_braces) { + #### No code on the line. + #&save_comment ($_); + next; + } + } else { + unless ($in_braces) { + #&save_comment ($_); + next; + } + } + } + + #### Strip C++-style comments. + if ($line =~ s%\s*//.*$%%) { + if ($line =~ /^\s*$/ && ! $in_braces) { + #### C++-style comment, without any code on the line. + #&save_comment ($_); + next; + } + } + + #### And C-style comments. + if ($line =~ m%/\*%) { + #### Begin C-style comment. Strip any complete comment(s), + #### then see what's left. + + $line =~ s%\s*/\*.*\*/\s*%%g; + + #### check to see if a preprocessor is on this line + if (! $in_braces) { + if ($line eq '') { + #### The line just had comment(s). Save it. + #&save_comment ($_); + next; + } else { + #### There's other text on the line. See if it's just the + #### start of a comment. + if ($line =~ m%/\*% && $line !~ m%\*/%) { + #### The C-style comment isn't terminated on this line. + $in_C_comment = 1; + #&save_comment ($_); + next; + } + } + } + } + + #### For now, skip ACE_RCSID's. Eventually, we might want to + #### consider putting them in _every_ file, if they're enabled. + next if $line =~ /^ACE_RCSID/; + + if ($in_braces) { + push @unknown, $_; + if ($line =~ /{/) { + ++$in_braces; + } elsif ($line =~ /^};/) { + #### }; at beginning of line could signify end of class + --$in_braces; + if ($in_braces == 0) { + push @intro, @unknown; + @unknown = (); + } + } elsif ($line =~ /^}/) { + #### } at beginning of line signifies end of function. + --$in_braces; + push @current_code, @unknown; + @unknown = (); + &finish_current ($basename, ++$current_file_number); + } elsif ($line =~ /};/) { + #### end of multi-line data delcaration + --$in_braces; + if ($in_braces == 0) { + push @current_code, @unknown; + @unknown = (); + &finish_current ($basename, ++$current_file_number); + } + } + } else { + #### Not in braces. + if (($line =~ m%[^/]*{%) && (! $preprocessor_continuation)) { + #### { signifies beginning of braces (obviously :). + if ($line =~ /};/) { + #### braces end on this line + push @unknown, $_; + push @current_code, @unknown; + @unknown = (); + &finish_current ($basename, ++$current_file_number); + } else { + push @unknown, $_; + $in_braces = 1; + $in_nonfunction_code = $top_of_file = 0; + } + } elsif ($line =~ /^}/) { + warn "$0: skipping unexpected } on line $. of \"$file\"\n"; + next; + } elsif ($line =~ /^typedef/) { + push @intro, $_; + } elsif ($line =~ /^\s*#/ || $preprocessor_continuation) { + #### Preprocessor directive. + if ($in_nonfunction_code) { + push @unknown, $_; + } else { + push @intro, $_; + } + $top_of_file = 0; + $preprocessor_continuation = /\\$/ ? 1 : 0; + + if ($line =~ m%^\s*#\s*if\s*(.*)(/.*)*$%) { + push @save_if, $_; + unshift @endif, "#endif /* $1 [Added by split-cpp.] */\n"; + + } elsif ($line =~ /^\s*#\s*endif/) { + #### End an #if/#else block. + unless (defined pop @save_if) { + pop @if; + if ($preserved_ifs > 0) { + --$preserved_ifs; + } + } + shift @endif; + + #### } elsif ($line =~ /^\s*#/) { + #### Any other preprocessor directive. + } + + } elsif ($line =~ /^\s*$/) { + #### Whitespace only, or empty line.. + push @current_code, "\n"; + if ($in_nonfunction_code) { + #### In the midst of non-function code, we reached a + #### blank line. Assume that we're done with it. + &finish_current ($basename, ++$current_file_number); + } else { + #### Not in a function, so add to intro. Just in case data or + #### a function follow it, flush now. + $preserved_ifs += $#save_if + 1; + &flush_current (\@intro); + } + + } elsif ($line =~ /;/) { + #### Data definition or semicolon-terminated macro invocation. + push @unknown, $_; + $top_of_file = 0; + + #### Is it file-static? Squash newlines out of @current_code. + my $statement = join (' ', @current_code); + if ($statement =~ /([^=[(]+)[=[(](.*)/) { + if ($1 =~ /static/) { + #### Move code to the intro. + push @intro, @current_comments; + @current_comments = (); + &flush_current (\@intro); + + #### Not separate code. + $in_nonfunction_code = 0; + + #### ???? Extract name from the left side and save for + #### later matching. + } else { + if ($statement =~ /^USEUNIT\s*\(/) { + #### Special-case those Borland USEUNIT things. + &flush_current (\@intro); + } else { + #### Non-static entity, with semicolon. Wrap it up. + push @current_code, @unknown; + @unknown = (); + &finish_current ($basename, ++$current_file_number); + } + } + } else { + #### Dunno. Wrap it up, anyways. + push @current_code, @unknown; + @unknown = (); + &finish_current ($basename, ++$current_file_number); + } + } else { + #### Beginning of data definition or function or class. + push @unknown, $_; + $in_nonfunction_code = 1; + $top_of_file = 0; + } + } + + if (eof) { + close (ARGV); #### To reset line number counter. + if ($#intro > $intro_length) { + #### Leftover prepreprocessor statement(s), such as #pragma + #### instantiate. + &finish_current ($basename, ++$current_file_number); + } + } + } + + close INPUT; + } +}; + + +#### +#### Save a comment in the appropriate array. +#### +#sub save_comment { +# my ($comment) = @_; +# +# if ($top_of_file) { +# push @intro, $comment; +# } else { +# push @current_comments, $comment; +# } +#} + + +#### +#### Flush the contents of the @current_code array to the destination +#### argument array. It is passed by reference. +#### +sub flush_current { + my ($destination) = @_; + + push @$destination, @current_code; + @current_code = (); +} + + +#### +#### Flush what we've got now to an output (split) file. +#### +sub finish_current { + my ($basename, $current_file_number) = @_; + + my $current_file_name = + sprintf "$split_dir$DIR_SEPARATOR${basename}_S%d.$extension", + $current_file_number++; + + if ($verbose) { + print "CURRENT OUTPUT FILE: $current_file_name\n"; + print "INTRO:\n"; + print @intro; + print @if; + print @current_comments; + print "CURRENT CODE:\n"; + print @current_code; + print @endif; + } + + open OUTPUT, "> $current_file_name" || + die "unable to open $current_file_name\n"; + + print OUTPUT "// Automatically generated by ACE's split-cpp.\n" . + "// DO NOT EDIT!\n\n"; + if ($debug) { + print OUTPUT "INTRO:\n", @intro, "IF:\n", @if, + "COMMENTS:\n", @current_comments, + "CURRENT:\n", @current_code, "ENDIF:\n", @endif; + } else { + print OUTPUT @intro, @if, @current_comments, @current_code, @endif; + } + + close OUTPUT; + + #### For detection of leftover preprocessor statements and + #### comments at end of file. + $intro_length = $#intro; + + @current_comments = @current_code = @save_if = (); + $in_braces = $in_nonfunction_code = 0; +} |