webengine/osswebengine/WebKitTools/S60Tools/prepare-ChangeLog.bat
changeset 0 dd21522fd290
equal deleted inserted replaced
-1:000000000000 0:dd21522fd290
       
     1 @rem = '--*-Perl-*--
       
     2 @echo off
       
     3 if "%OS%" == "Windows_NT" goto WinNT
       
     4 perl -x -S "%0" %1 %2 %3 %4 %5 %6 %7 %8 %9
       
     5 goto endofperl
       
     6 :WinNT
       
     7 perl -x -S %0 %*
       
     8 if NOT "%COMSPEC%" == "%SystemRoot%\system32\cmd.exe" goto endofperl
       
     9 if %errorlevel% == 9009 echo You do not have Perl in your PATH.
       
    10 if errorlevel 1 goto script_failed_so_exit_with_non_zero_val 2>nul
       
    11 goto endofperl
       
    12 @rem ';
       
    13 #!/usr/bin/perl -w
       
    14 #line 15
       
    15 # -*- Mode: perl; indent-tabs-mode: nil; c-basic-offset: 2  -*-
       
    16 
       
    17 #
       
    18 #  Copyright (C) 2000, 2001 Eazel, Inc.
       
    19 #  Copyright (C) 2002, 2003, 2004, 2005, 2006 Apple Computer, Inc.
       
    20 #
       
    21 #  prepare-ChangeLog is free software; you can redistribute it and/or
       
    22 #  modify it under the terms of the GNU General Public
       
    23 #  License as published by the Free Software Foundation; either
       
    24 #  version 2 of the License, or (at your option) any later version.
       
    25 #
       
    26 #  prepare-ChangeLog is distributed in the hope that it will be useful,
       
    27 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    28 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    29 #  General Public License for more details.
       
    30 #
       
    31 #  You should have received a copy of the GNU General Public
       
    32 #  License along with this program; if not, write to the Free
       
    33 #  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
       
    34 #
       
    35 
       
    36 
       
    37 # Perl script to create a ChangeLog entry with names of files
       
    38 # and functions from a diff.
       
    39 #
       
    40 # Darin Adler <darin@bentspoon.com>, started 20 April 2000
       
    41 # Java support added by Maciej Stachowiak <mjs@eazel.com>
       
    42 # Objective-C, C++ and Objective-C++ support added by Maciej Stachowiak <mjs@apple.com>
       
    43 # Minor changes for Win32 S60 environment by bradley.morrison@nokia.com
       
    44 
       
    45 #
       
    46 # TODO:
       
    47 #   List functions that have been removed too.
       
    48 #   Decide what a good logical order is for the changed files
       
    49 #     other than a normal text "sort" (top level first?)
       
    50 #     (group directories?) (.h before .c?)
       
    51 #   Handle yacc source files too (other languages?).
       
    52 #   Help merge when there are ChangeLog conflicts or if there's
       
    53 #     already a partly written ChangeLog entry.
       
    54 #   Add command line option to put the ChangeLog into a separate
       
    55 #     file or just spew it out stdout.
       
    56 #   Add SVN version numbers for commit (can't do that until
       
    57 #     the changes are checked in, though).
       
    58 #   Work around diff stupidity where deleting a function that starts
       
    59 #     with a comment makes diff think that the following function
       
    60 #     has been changed (if the following function starts with a comment
       
    61 #     with the same first line, such as /**)
       
    62 #   Work around diff stupidity where deleting an entire function and
       
    63 #     the blank lines before it makes diff think you've changed the
       
    64 #     previous function.
       
    65 
       
    66 use strict;
       
    67 use warnings;
       
    68 
       
    69 use File::Basename;
       
    70 use File::Spec;
       
    71 use File::Temp qw/ tempfile/;
       
    72 use Getopt::Long;
       
    73 use Cwd;
       
    74 use Win32;
       
    75 
       
    76 sub canonicalizePath($);
       
    77 sub get_function_line_ranges($$);
       
    78 sub get_function_line_ranges_for_c($$);
       
    79 sub get_function_line_ranges_for_java($$);
       
    80 sub method_decl_to_selector($);
       
    81 sub processPaths(\@);
       
    82 
       
    83 my $openChangeLogs = 0;
       
    84 my $showHelp = 0;
       
    85 my $spewDiff = $ENV{"PREPARE_CHANGELOG_DIFF"};
       
    86 my $parseOptionsResult =
       
    87     GetOptions("diff|d!" => \$spewDiff,
       
    88                "help|h!" => \$showHelp,
       
    89                "open|o!" => \$openChangeLogs);
       
    90 if (!$parseOptionsResult || $showHelp)
       
    91   {
       
    92     print STDERR basename($0) . " [-d|--diff] [-h|--help] [-o|--open] [svndir1 [svndir2 ...]]\n";
       
    93     print STDERR "  -d|--diff  Spew diff to stdout when running\n";
       
    94     print STDERR "  -h|--help  Show this help message\n";
       
    95     print STDERR "  -o|--open  Open ChangeLogs in an editor when done\n";
       
    96     exit 1;
       
    97   }
       
    98 
       
    99 my %paths = processPaths(@ARGV);
       
   100 
       
   101 # Find the list of modified files
       
   102 my @changed_files;
       
   103 my $changed_files_string;
       
   104 my %changed_line_ranges;
       
   105 my %function_lists;
       
   106 my @conflict_files;
       
   107 
       
   108 my $SVN = "svn";
       
   109 
       
   110 my %statusDescription = (
       
   111     "A" => " Added.",
       
   112     "D" => " Removed.",
       
   113     "M" => "",
       
   114     "R" => " Replaced.",
       
   115 );
       
   116 
       
   117 my $changedLayoutTests = 0;
       
   118 
       
   119 my ($DIFFOUT,$diffTempFile)= tempfile(TEMPLATE => basename($0) . "-XXXXXXXX",
       
   120                              DIR => ($ENV{'TMPDIR'} || $ENV{'TEMP'} || "/tmp"),
       
   121                              SUFFIX => ".diff");
       
   122 
       
   123 my @diffFiles;
       
   124 
       
   125 print STDERR "  Running 'svn diff' to find changed, added, or removed files.\n";
       
   126 open SVNDIFF, "$SVN diff --diff-cmd diff -x -N " . join("' '", keys %paths) . "|"
       
   127     or die "The svn diff failed: $!.\n";
       
   128 while (<SVNDIFF>)
       
   129   {
       
   130     print $DIFFOUT $_;
       
   131     if (/^Index: (.+)$/)
       
   132       {
       
   133         push @diffFiles, $1;
       
   134       }
       
   135   }
       
   136 close SVNDIFF;
       
   137 close $DIFFOUT;
       
   138 
       
   139 if (@diffFiles)
       
   140   {
       
   141     my $diffFilesString = join (' ', @diffFiles);
       
   142     print STDERR "  Running 'svn stat' on changed, added, or removed files.\n";
       
   143     open SVNSTAT, "$SVN stat $diffFilesString |" or die "The svn stat failed: $!.\n";
       
   144     while(<SVNSTAT>)
       
   145       {
       
   146         if (/^([A-Z]).+\s+(.+)$/)
       
   147           {
       
   148               my $status = $1;
       
   149               my $file = $2;
       
   150               $file =~ s#\\#/#g;
       
   151               if ($status eq "A" || $status eq "M")
       
   152                 {
       
   153                   my @components = File::Spec->splitdir($file);
       
   154                   $changedLayoutTests = 1 if $components[0] eq "LayoutTests";
       
   155                   push @changed_files, $file if $components[$#components] ne "ChangeLog";
       
   156                 }
       
   157               push @conflict_files, $file if $status eq "C";
       
   158               $function_lists{$file} = $statusDescription{$status} if exists $statusDescription{$status};
       
   159           }
       
   160         else
       
   161           {
       
   162             print;  # error output from svn stat
       
   163           }
       
   164       }
       
   165     close SVNSTAT;
       
   166   }
       
   167 
       
   168 if (!@diffFiles || !%function_lists)
       
   169   {
       
   170     print STDERR "  No changes found.\n";
       
   171     exit 1;
       
   172   }
       
   173 
       
   174 if (@conflict_files)
       
   175   {
       
   176     print STDERR "  The following files have conflicts. Run prepare-ChangeLog again after fixing the conflicts:\n";
       
   177     print STDERR join("\n", @conflict_files), "\n";
       
   178     exit 1;
       
   179   }
       
   180 
       
   181 if (@changed_files)
       
   182   {
       
   183     $changed_files_string = "'" . join ("' '", @changed_files) . "'";
       
   184 
       
   185     # For each file, build a list of modified lines.
       
   186     # Use line numbers from the "after" side of each diff.
       
   187     print STDERR "  Reviewing 'svn diff' to determine which lines changed.\n";
       
   188     my $file;
       
   189     open DIFF, "< $diffTempFile" or die "Opening $diffTempFile failed: $!.\n";
       
   190     while (<DIFF>)
       
   191       {
       
   192         $file = $1 if /^Index: (\S+)$/;
       
   193         if (defined $file) {
       
   194           if (/^\d+(,\d+)?[acd](\d+)(,(\d+))?/) {
       
   195             push @{$changed_line_ranges{$file}}, [ $2, $4 || $2 ];
       
   196           } elsif (/DO_NOT_COMMIT/) {
       
   197             print STDERR "WARNING: file $file contains the string DO_NOT_COMMIT, line $.\n";
       
   198           }
       
   199         }
       
   200       }
       
   201     close DIFF;
       
   202   }
       
   203 
       
   204 # For each source file, convert line range to function list.
       
   205 if (%changed_line_ranges)
       
   206   {
       
   207     print STDERR "  Extracting affected function names from source files.\n";
       
   208     foreach my $file (keys %changed_line_ranges)
       
   209       {
       
   210         # Only look for function names in .c files.
       
   211         next unless $file =~ /\.(c|cpp|m|mm|h|java)/;
       
   212     
       
   213         # Find all the functions in the file.
       
   214         open SOURCE, $file or next;
       
   215         my @function_ranges = get_function_line_ranges(\*SOURCE, $file);
       
   216         close SOURCE;
       
   217     
       
   218         # Find all the modified functions.
       
   219         my @functions;
       
   220         my %saw_function;
       
   221         my @change_ranges = (@{$changed_line_ranges{$file}}, []);
       
   222         my @change_range = (0, 0);
       
   223         FUNCTION: foreach my $function_range_ref (@function_ranges)
       
   224           {
       
   225             my @function_range = @$function_range_ref;
       
   226     
       
   227             # Advance to successive change ranges.
       
   228             for (;; @change_range = @{shift @change_ranges})
       
   229               {
       
   230                 last FUNCTION unless @change_range;
       
   231     
       
   232                 # If past this function, move on to the next one.
       
   233                 next FUNCTION if $change_range[0] > $function_range[1];
       
   234     
       
   235                 # If an overlap with this function range, record the function name.
       
   236                 if ($change_range[1] >= $function_range[0]
       
   237                     and $change_range[0] <= $function_range[1])
       
   238                   {
       
   239                     if (!$saw_function{$function_range[2]})
       
   240                       {
       
   241                         $saw_function{$function_range[2]} = 1;
       
   242                         push @functions, $function_range[2];
       
   243                       }
       
   244                     next FUNCTION;
       
   245                   }
       
   246               }
       
   247           }
       
   248     
       
   249         # Format the list of functions now.
       
   250 
       
   251         if (@functions) {
       
   252             $function_lists{$file} = "" if !defined $function_lists{$file};
       
   253             $function_lists{$file} .= "\n        (" . join("):\n        (", @functions) . "):";
       
   254         }
       
   255       }
       
   256   }
       
   257 
       
   258 my $name = $ENV{USERNAME}
       
   259   || $ENV{REAL_NAME}
       
   260   || Win32::LoginName()
       
   261   || "set REAL_NAME environment variable";
       
   262 # Remove trailing parenthesized notes from user name (bit of hack).
       
   263 $name =~ s/\(.*?\)\s*$//g;
       
   264 
       
   265 # Find the change logs.
       
   266 my %has_log;
       
   267 my %files;
       
   268 foreach my $file (sort keys %function_lists)
       
   269   {
       
   270     my $prefix = $file;
       
   271     my $has_log = 0;
       
   272     while ($prefix)
       
   273       {
       
   274         $prefix =~ s-/[^/]+/?$-/- or $prefix = "";
       
   275         $has_log = $has_log{$prefix};
       
   276         if (!defined $has_log)
       
   277           {
       
   278             $has_log = -f "${prefix}ChangeLog";
       
   279             $has_log{$prefix} = $has_log;
       
   280           }
       
   281         last if $has_log;
       
   282       }
       
   283     if (!$has_log)
       
   284       {
       
   285         print STDERR "No ChangeLog found for $file.\n";
       
   286       }
       
   287     else
       
   288       {
       
   289         push @{$files{$prefix}}, $file;
       
   290       }
       
   291   }
       
   292 
       
   293 # Get the latest ChangeLog files from svn.
       
   294 my $logs = "";
       
   295 foreach my $prefix (sort keys %files)
       
   296   {
       
   297     $logs .= " ${prefix}ChangeLog";
       
   298   }
       
   299 if ($logs)
       
   300   {
       
   301     print STDERR "  Running 'svn update' to update ChangeLog files.\n";
       
   302     open ERRORS, "$SVN update -q$logs |" or die "The svn update of ChangeLog files failed: $!.\n";
       
   303     print STDERR "    $_" while <ERRORS>;
       
   304     close ERRORS;
       
   305   }
       
   306 
       
   307 # Write out a new ChangeLog file.
       
   308 foreach my $prefix (sort keys %files)
       
   309   {
       
   310     print STDERR "Editing the ${prefix}ChangeLog file.\n\n";
       
   311     open OLD_CHANGE_LOG, "${prefix}ChangeLog" or die "Could not open ${prefix}ChangeLog file: $!.\n";
       
   312     # It's less efficient to read the whole thing into memory than it would be
       
   313     # to read it while we prepend to it later, but I like doing this part first.
       
   314     my @old_change_log = <OLD_CHANGE_LOG>;
       
   315     close OLD_CHANGE_LOG;
       
   316     open CHANGE_LOG, "> ${prefix}ChangeLog" or die "Could not write ${prefix}ChangeLog\n.";
       
   317     print CHANGE_LOG "$name, reviewed by <reviewer>\n";
       
   318     print CHANGE_LOG " DESC: \n";
       
   319     print CHANGE_LOG " http://bugs.webkit.org/show_bug.cgi?id=\n\n";
       
   320     if ($prefix =~ m/WebCore/ || cwd() =~ m/WebCore/) {
       
   321         print CHANGE_LOG "        WARNING: NO TEST CASES ADDED OR CHANGED\n\n" unless $changedLayoutTests;
       
   322     }
       
   323 
       
   324     foreach my $file (sort @{$files{$prefix}})
       
   325       {
       
   326         my $file_stem = substr $file, length $prefix;
       
   327         print CHANGE_LOG "        * $file_stem:$function_lists{$file}\n";
       
   328       }
       
   329     print CHANGE_LOG "\n", @old_change_log;
       
   330     close CHANGE_LOG;
       
   331   }
       
   332 
       
   333 # Write out another diff.
       
   334 if ($spewDiff && @changed_files)
       
   335   {
       
   336     print STDERR "  Running 'svn diff' to help you write the ChangeLog entries.\n";
       
   337     open DIFF, "$SVN diff $changed_files_string |" or die "The svn diff failed: $!.\n";
       
   338     while (<DIFF>) { print; }
       
   339     close DIFF;
       
   340   }
       
   341 
       
   342 # Open ChangeLogs.
       
   343 if ($openChangeLogs && $logs)
       
   344   {
       
   345     print STDERR "  Opening the edited ChangeLog files.\n";
       
   346     my $editor = $ENV{"CHANGE_LOG_EDIT_APPLICATION"};
       
   347     if ($editor) {
       
   348         system "open -a '$editor'$logs";
       
   349     } else {
       
   350         system "open -e$logs";
       
   351     }
       
   352   }
       
   353 
       
   354 # Done.
       
   355 exit;
       
   356 
       
   357 sub prompt_for {
       
   358    my ($msg) = @_;
       
   359    print $msg, ": ";
       
   360    ReadMode('normal');
       
   361    my $result = ReadLine(0);
       
   362 #   print "\n";
       
   363    return $result  
       
   364 }
       
   365 
       
   366 sub canonicalizePath($)
       
   367   {
       
   368     my ($file) = @_;
       
   369 
       
   370     # Remove extra slashes and '.' directories in path
       
   371     $file = File::Spec->canonpath($file);
       
   372 
       
   373     # Remove '..' directories in path
       
   374     my @dirs = ();
       
   375     foreach my $dir (File::Spec->splitdir($file))
       
   376       {
       
   377         if ($dir eq '..' && $#dirs >= 0 && $dirs[$#dirs] ne '..')
       
   378           {
       
   379             pop(@dirs);
       
   380           }
       
   381         else
       
   382           {
       
   383             push(@dirs, $dir);
       
   384           }
       
   385       }
       
   386     return ($#dirs >= 0) ? File::Spec->catdir(@dirs) : ".";
       
   387   }
       
   388 
       
   389 sub get_function_line_ranges($$)
       
   390   {
       
   391     my ($file_handle, $file_name) = @_;
       
   392 
       
   393     if ($file_name =~ /\.(c|cpp|m|mm|h)$/) {
       
   394         return get_function_line_ranges_for_c ($file_handle, $file_name);
       
   395     } elsif ($file_name =~ /\.java$/) {
       
   396         return get_function_line_ranges_for_java ($file_handle, $file_name);
       
   397     }
       
   398     return ();
       
   399   }
       
   400 
       
   401 
       
   402 sub method_decl_to_selector($)
       
   403   {
       
   404     (my $method_decl) = @_;
       
   405 
       
   406     $_ = $method_decl;
       
   407 
       
   408     if ((my $comment_stripped) = m-([^/]*)(//|/*).*-) 
       
   409       {
       
   410         $_ = $comment_stripped;
       
   411       }
       
   412 
       
   413     s/,\s*...//;
       
   414 
       
   415     if (/:/) 
       
   416       {
       
   417         my @components = split /:/;
       
   418         pop @components if (scalar @components > 1);
       
   419         $_ = (join ':', map {s/.*[^[:word:]]//; scalar $_;} @components) . ':';
       
   420       } else {
       
   421         s/\s*$//;
       
   422         s/.*[^[:word:]]//;
       
   423       }
       
   424 
       
   425     return $_;
       
   426   }
       
   427 
       
   428 
       
   429 
       
   430 # Read a file and get all the line ranges of the things that look like C functions.
       
   431 # A function name is the last word before an open parenthesis before the outer
       
   432 # level open brace. A function starts at the first character after the last close
       
   433 # brace or semicolon before the function name and ends at the close brace.
       
   434 # Comment handling is simple-minded but will work for all but pathological cases.
       
   435 #
       
   436 # Result is a list of triples: [ start_line, end_line, function_name ].
       
   437 
       
   438 sub get_function_line_ranges_for_c($$)
       
   439   {
       
   440     my ($file_handle, $file_name) = @_;
       
   441 
       
   442     my @ranges;
       
   443 
       
   444     my $in_comment = 0;
       
   445     my $in_macro = 0;
       
   446     my $in_method_declaration = 0;
       
   447     my $in_parentheses = 0;
       
   448     my $in_braces = 0;
       
   449     my $brace_start = 0;
       
   450     my $brace_end = 0;
       
   451     my $skip_til_brace_or_semicolon = 0;
       
   452 
       
   453     my $word = "";
       
   454     my $interface_name = "";
       
   455 
       
   456     my $potential_method_char = "";
       
   457     my $potential_method_spec = "";
       
   458 
       
   459     my $potential_start = 0;
       
   460     my $potential_name = "";
       
   461 
       
   462     my $start = 0;
       
   463     my $name = "";
       
   464 
       
   465     my $next_word_could_be_namespace = 0;
       
   466     my $potential_namespace = "";
       
   467     my @namespaces;
       
   468 
       
   469     while (<$file_handle>)
       
   470       {
       
   471         # Handle continued multi-line comment.
       
   472         if ($in_comment)
       
   473           {
       
   474             next unless s-.*\*/--;
       
   475             $in_comment = 0;
       
   476           }
       
   477 
       
   478         # Handle continued macro.
       
   479         if ($in_macro)
       
   480           {
       
   481             $in_macro = 0 unless /\\$/;
       
   482             next;
       
   483           }
       
   484 
       
   485         # Handle start of macro (or any preprocessor directive).
       
   486         if (/^\s*\#/)
       
   487           {
       
   488             $in_macro = 1 if /^([^\\]|\\.)*\\$/;
       
   489             next;
       
   490           }
       
   491 
       
   492         # Handle comments and quoted text.
       
   493         while (m-(/\*|//|\'|\")-) # \' and \" keep emacs perl mode happy
       
   494           {
       
   495             my $match = $1;
       
   496             if ($match eq "/*")
       
   497               {
       
   498                 if (!s-/\*.*?\*/--)
       
   499                   {
       
   500                     s-/\*.*--;
       
   501                     $in_comment = 1;
       
   502                   }
       
   503               }
       
   504             elsif ($match eq "//")
       
   505               {
       
   506                 s-//.*--;
       
   507               }
       
   508             else # ' or "
       
   509               {
       
   510                 if (!s-$match([^\\]|\\.)*?$match--)
       
   511                   {
       
   512                     warn "mismatched quotes at line $. in $file_name\n";
       
   513                     s-$match.*--;
       
   514                   }
       
   515               }
       
   516           }
       
   517 
       
   518 
       
   519         # continued method declaration
       
   520         if ($in_method_declaration) 
       
   521           {
       
   522               my $original = $_;
       
   523               my $method_cont = $_;
       
   524 
       
   525               chomp $method_cont;
       
   526               $method_cont =~ s/[;\{].*//;
       
   527               $potential_method_spec = "${potential_method_spec} ${method_cont}";
       
   528 
       
   529               $_ = $original;
       
   530               if (/;/) 
       
   531                 {
       
   532                   $potential_start = 0;
       
   533                   $potential_method_spec = "";
       
   534                   $potential_method_char = "";
       
   535                   $in_method_declaration = 0;
       
   536                   s/^[^;\{]*//;
       
   537                 } elsif (/{/) {
       
   538                   my $selector = method_decl_to_selector ($potential_method_spec);
       
   539                   $potential_name = "${potential_method_char}\[${interface_name} ${selector}\]";
       
   540                   
       
   541                   $potential_method_spec = "";
       
   542                   $potential_method_char = "";
       
   543                   $in_method_declaration = 0;
       
   544   
       
   545                   $_ = $original;
       
   546                   s/^[^;{]*//;
       
   547                 } else {
       
   548                   next;
       
   549                 }
       
   550           }
       
   551 
       
   552         
       
   553         # start of method declaration
       
   554         if ((my $method_char, my $method_spec) = m&^([-+])([^0-9;][^;]*);?$&)
       
   555           {
       
   556             my $original = $_;
       
   557 
       
   558             if ($interface_name) 
       
   559               {
       
   560                 chomp $method_spec;
       
   561                 $method_spec =~ s/\{.*//;
       
   562             
       
   563                 $potential_method_char = $method_char;
       
   564                 $potential_method_spec = $method_spec;
       
   565                 $potential_start = $.;
       
   566                 $in_method_declaration = 1;
       
   567               } else { 
       
   568                 warn "declaring a method but don't have interface on line $. in $file_name\n";
       
   569               }
       
   570             $_ = $original;
       
   571             if (/\{/) {
       
   572               my $selector = method_decl_to_selector ($potential_method_spec);
       
   573               $potential_name = "${potential_method_char}\[${interface_name} ${selector}\]";
       
   574               
       
   575               $potential_method_spec = "";
       
   576               $potential_method_char = "";
       
   577               $in_method_declaration = 0;
       
   578               $_ = $original;
       
   579               s/^[^{]*//;
       
   580             } else {
       
   581               next;
       
   582             }
       
   583           }
       
   584 
       
   585 
       
   586         # Find function, interface and method names.
       
   587         while (m&((?:[[:word:]]+::)*operator(?:[ \t]*\(\)|[^()]*)|[[:word:]:~]+|[(){}:;])|\@(?:implementation|interface|protocol)\s+(\w+)[^{]*&g)
       
   588           {
       
   589             # interface name
       
   590             if ($2) 
       
   591               {
       
   592                 $interface_name = $2;
       
   593                 next;
       
   594               }
       
   595 
       
   596             # Open parenthesis.
       
   597             if ($1 eq "(")
       
   598               {
       
   599                 $potential_name = $word unless $in_parentheses || $skip_til_brace_or_semicolon;
       
   600                 $in_parentheses++;
       
   601                 next;
       
   602               }
       
   603 
       
   604             # Close parenthesis.
       
   605             if ($1 eq ")")
       
   606               {
       
   607                 $in_parentheses--;
       
   608                 next;
       
   609               }
       
   610 
       
   611             # C++ constructor initializers
       
   612             if ($1 eq ":")
       
   613               {
       
   614                   $skip_til_brace_or_semicolon = 1 unless ($in_parentheses || $in_braces);
       
   615               }
       
   616 
       
   617             # Open brace.
       
   618             if ($1 eq "{")
       
   619               {
       
   620                 $skip_til_brace_or_semicolon = 0;
       
   621 
       
   622                 if ($potential_namespace) {
       
   623                     push @namespaces, $potential_namespace;
       
   624                     $potential_namespace = "";
       
   625                     next;
       
   626                 }
       
   627 
       
   628                 # Promote potential name to real function name at the
       
   629                 # start of the outer level set of braces (function body?).
       
   630                 if (!$in_braces and $potential_start)
       
   631                   {
       
   632                     $start = $potential_start;
       
   633                     $name = $potential_name;
       
   634                     if (@namespaces && (length($name) < 2 || substr($name,1,1) ne "[")) {
       
   635                         $name = join ('::', @namespaces, $name);
       
   636                     }
       
   637                   }
       
   638 
       
   639                 $in_method_declaration = 0;
       
   640 
       
   641                 $brace_start = $. if (!$in_braces);
       
   642                 $in_braces++;
       
   643                 next;
       
   644               }
       
   645 
       
   646             # Close brace.
       
   647             if ($1 eq "}")
       
   648               {
       
   649                 if (!$in_braces && @namespaces) {
       
   650                     pop @namespaces;
       
   651                     next;
       
   652                 }
       
   653 
       
   654                 $in_braces--;
       
   655                 $brace_end = $. if (!$in_braces);
       
   656 
       
   657                 # End of an outer level set of braces.
       
   658                 # This could be a function body.
       
   659                 if (!$in_braces and $name)
       
   660                   {
       
   661                     push @ranges, [ $start, $., $name ];
       
   662                     $name = "";
       
   663                   }
       
   664 
       
   665                 $potential_start = 0;
       
   666                 $potential_name = "";
       
   667                 next;
       
   668               }
       
   669 
       
   670             # Semicolon.
       
   671             if ($1 eq ";")
       
   672               {
       
   673                 $skip_til_brace_or_semicolon = 0;
       
   674                 $potential_start = 0;
       
   675                 $potential_name = "";
       
   676                 $in_method_declaration = 0;
       
   677                 next;
       
   678               }
       
   679 
       
   680             # Ignore "const" method qualifier.
       
   681             if ($1 eq "const") {
       
   682                 next;
       
   683             }
       
   684 
       
   685             if ($1 eq "namespace" || $1 eq "class" || $1 eq "struct") {
       
   686                 $next_word_could_be_namespace = 1;
       
   687                 next;
       
   688             }
       
   689 
       
   690             # Word.
       
   691             $word = $1;
       
   692             if (!$skip_til_brace_or_semicolon) {
       
   693               if ($next_word_could_be_namespace) {
       
   694                 $potential_namespace = $word;
       
   695                 $next_word_could_be_namespace = 0;
       
   696               } elsif ($potential_namespace) {
       
   697                 $potential_namespace = "";
       
   698               }
       
   699 
       
   700               if (!$in_parentheses) {
       
   701                 $potential_start = 0;
       
   702                 $potential_name = "";
       
   703               }
       
   704               if (!$potential_start) {
       
   705                 $potential_start = $.;
       
   706                 $potential_name = "";
       
   707               }
       
   708             }
       
   709           }
       
   710       }
       
   711 
       
   712     warn "missing close braces in $file_name (probable start at $brace_start)\n" if ($in_braces > 0);
       
   713     warn "too many close braces in $file_name (probable start at $brace_end)\n" if ($in_braces < 0);
       
   714 
       
   715     warn "mismatched parentheses in $file_name\n" if $in_parentheses;
       
   716 
       
   717     return @ranges;
       
   718   }
       
   719 
       
   720 
       
   721 
       
   722 # Read a file and get all the line ranges of the things that look like Java
       
   723 # classes, interfaces and methods.
       
   724 #
       
   725 # A class or interface name is the word that immediately follows
       
   726 # `class' or `interface' when followed by an open curly brace and not
       
   727 # a semicolon. It can appear at the top level, or inside another class
       
   728 # or interface block, but not inside a function block
       
   729 #
       
   730 # A class or interface starts at the first character after the first close
       
   731 # brace or after the function name and ends at the close brace.
       
   732 #
       
   733 # A function name is the last word before an open parenthesis before
       
   734 # an open brace rather than a semicolon. It can appear at top level or
       
   735 # inside a class or interface block, but not inside a function block.
       
   736 #
       
   737 # A function starts at the first character after the first close
       
   738 # brace or after the function name and ends at the close brace.
       
   739 #
       
   740 # Comment handling is simple-minded but will work for all but pathological cases.
       
   741 #
       
   742 # Result is a list of triples: [ start_line, end_line, function_name ].
       
   743 
       
   744 sub get_function_line_ranges_for_java($$)
       
   745   {
       
   746     my ($file_handle, $file_name) = @_;
       
   747 
       
   748     my @current_scopes;
       
   749 
       
   750     my @ranges;
       
   751 
       
   752     my $in_comment = 0;
       
   753     my $in_macro = 0;
       
   754     my $in_parentheses = 0;
       
   755     my $in_braces = 0;
       
   756     my $in_non_block_braces = 0;
       
   757     my $class_or_interface_just_seen = 0;
       
   758 
       
   759     my $word = "";
       
   760 
       
   761     my $potential_start = 0;
       
   762     my $potential_name = "";
       
   763     my $potential_name_is_class_or_interface = 0;
       
   764 
       
   765     my $start = 0;
       
   766     my $name = "";
       
   767     my $current_name_is_class_or_interface = 0;
       
   768 
       
   769     while (<$file_handle>)
       
   770       {
       
   771         # Handle continued multi-line comment.
       
   772         if ($in_comment)
       
   773           {
       
   774             next unless s-.*\*/--;
       
   775             $in_comment = 0;
       
   776           }
       
   777 
       
   778         # Handle continued macro.
       
   779         if ($in_macro)
       
   780           {
       
   781             $in_macro = 0 unless /\\$/;
       
   782             next;
       
   783           }
       
   784 
       
   785         # Handle start of macro (or any preprocessor directive).
       
   786         if (/^\s*\#/)
       
   787           {
       
   788             $in_macro = 1 if /^([^\\]|\\.)*\\$/;
       
   789             next;
       
   790           }
       
   791 
       
   792         # Handle comments and quoted text.
       
   793         while (m-(/\*|//|\'|\")-) # \' and \" keep emacs perl mode happy
       
   794           {
       
   795             my $match = $1;
       
   796             if ($match eq "/*")
       
   797               {
       
   798                 if (!s-/\*.*?\*/--)
       
   799                   {
       
   800                     s-/\*.*--;
       
   801                     $in_comment = 1;
       
   802                   }
       
   803               }
       
   804             elsif ($match eq "//")
       
   805               {
       
   806                 s-//.*--;
       
   807               }
       
   808             else # ' or "
       
   809               {
       
   810                 if (!s-$match([^\\]|\\.)*?$match--)
       
   811                   {
       
   812                     warn "mismatched quotes at line $. in $file_name\n";
       
   813                     s-$match.*--;
       
   814                   }
       
   815               }
       
   816           }
       
   817 
       
   818         # Find function names.
       
   819         while (m-(\w+|[(){};])-g)
       
   820           {
       
   821             # Open parenthesis.
       
   822             if ($1 eq "(")
       
   823               {
       
   824                 if (!$in_parentheses) {
       
   825                     $potential_name = $word;
       
   826                     $potential_name_is_class_or_interface = 0;
       
   827                 }
       
   828                 $in_parentheses++;
       
   829                 next;
       
   830               }
       
   831 
       
   832             # Close parenthesis.
       
   833             if ($1 eq ")")
       
   834               {
       
   835                 $in_parentheses--;
       
   836                 next;
       
   837               }
       
   838 
       
   839             # Open brace.
       
   840             if ($1 eq "{")
       
   841               {
       
   842                 # Promote potential name to real function name at the
       
   843                 # start of the outer level set of braces (function/class/interface body?).
       
   844                 if (!$in_non_block_braces
       
   845                     and (!$in_braces or $current_name_is_class_or_interface)
       
   846                     and $potential_start)
       
   847                   {
       
   848                     if ($name)
       
   849                       {
       
   850                           push @ranges, [ $start, ($. - 1),
       
   851                                           join ('.', @current_scopes) ];
       
   852                       }
       
   853 
       
   854 
       
   855                     $current_name_is_class_or_interface = $potential_name_is_class_or_interface;
       
   856 
       
   857                     $start = $potential_start;
       
   858                     $name = $potential_name;
       
   859 
       
   860                     push (@current_scopes, $name);
       
   861                   } else {
       
   862                       $in_non_block_braces++;
       
   863                   }
       
   864 
       
   865                 $potential_name = "";
       
   866                 $potential_start = 0;
       
   867 
       
   868                 $in_braces++;
       
   869                 next;
       
   870               }
       
   871 
       
   872             # Close brace.
       
   873             if ($1 eq "}")
       
   874               {
       
   875                 $in_braces--;
       
   876 
       
   877                 # End of an outer level set of braces.
       
   878                 # This could be a function body.
       
   879                 if (!$in_non_block_braces)
       
   880                   {
       
   881                     if ($name)
       
   882                       {
       
   883                         push @ranges, [ $start, $.,
       
   884                                         join ('.', @current_scopes) ];
       
   885 
       
   886                         pop (@current_scopes);
       
   887 
       
   888                         if (@current_scopes)
       
   889                           {
       
   890                             $current_name_is_class_or_interface = 1;
       
   891 
       
   892                             $start = $. + 1;
       
   893                             $name =  $current_scopes[$#current_scopes-1];
       
   894                           }
       
   895                         else
       
   896                           {
       
   897                             $current_name_is_class_or_interface = 0;
       
   898                             $start = 0;
       
   899                             $name =  "";
       
   900                           }
       
   901                     }
       
   902                   }
       
   903                 else
       
   904                   {
       
   905                     $in_non_block_braces-- if $in_non_block_braces;
       
   906                   }
       
   907 
       
   908                 $potential_start = 0;
       
   909                 $potential_name = "";
       
   910                 next;
       
   911               }
       
   912 
       
   913             # Semicolon.
       
   914             if ($1 eq ";")
       
   915               {
       
   916                 $potential_start = 0;
       
   917                 $potential_name = "";
       
   918                 next;
       
   919               }
       
   920 
       
   921             if ($1 eq "class" or $1 eq "interface")
       
   922               {
       
   923                 $class_or_interface_just_seen = 1;
       
   924                 next;
       
   925               }
       
   926 
       
   927             # Word.
       
   928             $word = $1;
       
   929             if (!$in_parentheses)
       
   930               {
       
   931                 if ($class_or_interface_just_seen) {
       
   932                     $potential_name = $word;
       
   933                     $potential_start = $.;
       
   934                     $class_or_interface_just_seen = 0;
       
   935                     $potential_name_is_class_or_interface = 1;
       
   936                     next;
       
   937                 }
       
   938               }
       
   939             if (!$potential_start)
       
   940               {
       
   941                 $potential_start = $.;
       
   942                 $potential_name = "";
       
   943               }
       
   944             $class_or_interface_just_seen = 0;
       
   945           }
       
   946       }
       
   947 
       
   948     warn "mismatched braces in $file_name\n" if $in_braces;
       
   949     warn "mismatched parentheses in $file_name\n" if $in_parentheses;
       
   950 
       
   951     return @ranges;
       
   952   }
       
   953 
       
   954 sub processPaths(\@)
       
   955   {
       
   956     my ($paths) = @_;
       
   957     return ("." => 1) if (!@{$paths});
       
   958 
       
   959     my %result = ();
       
   960 
       
   961     for my $file (@{$paths})
       
   962       {
       
   963         die "can't handle absolute paths like \"$file\"\n" if File::Spec->file_name_is_absolute($file);
       
   964         die "can't handle empty string path\n" if $file eq "";
       
   965         die "can't handle path with single quote in the name like \"$file\"\n" if $file =~ /'/; # ' (keep Xcode syntax highlighting happy)
       
   966 
       
   967         my $untouchedFile = $file;
       
   968 
       
   969         $file = canonicalizePath($file);
       
   970 
       
   971         die "can't handle paths with .. like \"$untouchedFile\"\n" if $file =~ m|/\.\./|;
       
   972 
       
   973         $result{$file} = 1;
       
   974       }
       
   975 
       
   976     return ("." => 1) if ($result{"."});
       
   977 
       
   978     # Remove any paths that also have a parent listed.
       
   979     for my $path (keys %result)
       
   980       {
       
   981         for (my $parent = dirname($path); $parent ne '.'; $parent = dirname($parent))
       
   982          {
       
   983             if ($result{$parent})
       
   984               {
       
   985                 delete $result{$path};
       
   986                 last;
       
   987               }
       
   988           }
       
   989       }
       
   990 
       
   991     return %result;
       
   992   }
       
   993 
       
   994 __END__
       
   995 :endofperl