tags:

views:

186

answers:

3

The script below takes function names in a text file and scans on a folder that contains multiple c,h files. It opens those files one-by-one and reads each line. If the match is found in any part of the files, it prints the line number and the line that contains the match.

Everything is working fine except that the comparison is not working properly. I would be very grateful to whoever solves my problem.

  #program starts:

  use FileHandle;
  print "ENTER THE PATH OF THE FILE THAT CONTAINS THE FUNCTIONS THAT YOU WANT TO     
  SEARCH: ";#getting the input file
  our $input_path = <STDIN>;
  $input_path =~ s/\s+$//;
  open(FILE_R1,'<',"$input_path") ||  die "File open failed!"; 
  print "ENTER THE PATH OF THE FUNCTION MODEL: ";#getting the folder path that 
                                                 #contains multiple .c,.h files
  our $model_path = <STDIN>;
  $model_path =~ s/\s+$//;
  our $last_dir = uc(substr ( $model_path,rindex( $model_path, "\\" ) +1 ));
  our $output = $last_dir."_FUNC_file_names";

  while(our $func_name_input = <FILE_R1> )#$func_name_input is the function name 
                                          #that is taken as the input
  {
     $func_name_input=reverse($func_name_input);
     $func_name_input=substr($func_name_input,rindex($func_name_input,"\("+1);
     $func_name_input=reverse($func_name_input);
     $func_name_input=substr($func_name_input,index($func_name_input," ")+1);

     #above 4 lines are func_name_input is choped and only part of the function 
     #name is taken. 

     opendir FUNC_MODEL,$model_path;
     while (our $file = readdir(FUNC_MODEL))
     {
        next if($file !~ m/\.(c|h)/i);
        find_func($file);    
     }
     close(FUNC_MODEL);
  }


  sub find_func()
  {
     my $fh1 = FileHandle->new("$model_path//$file") or die "ERROR: $!";

     while (!$fh1->eof())
     {
         my $func_name = $fh1->getline(); #getting the line

         **if($func_name =~$func_name_input)**#problem here it does not take the  
                                              #match
         {
             next if($func_name=~m/^\s+/);
             print "$.,$func_name\n";
         }
      }
   }
+2  A: 
$func_name_input=substr($func_name_input,rindex($func_name_input,"\("+1);

You're missing an ending parenthesis. Should be:

$func_name_input=substr($func_name_input,rindex($func_name_input,"\(")+1);

There's probably an easier way than those four statements, too. But it's a little early to wrap my head around it all. Do you want to match "foo" in "function foo() {"? If so, you could use a regex like /\s+([^) ]+)/.


When you say $func_name =~$func_name_input, you're treating all characters in $func_name_input as special regex characters. If this is not what you mean to do, you can use quotemeta (perldoc -f quotemeta): $func_name =~quotemeta($func_name_input) or $func_name =~ qr/\Q$func_name_input\E/.


Debugging will be easier with strictures (and a syntax-hilighting editor). Also note that, if you're not using those variables in other files, "our" doesn't do anything "my" wouldn't do for file-scoped variables.

Anonymous
+1  A: 

find + xargs + grep does 90% of what you want.

find . -name '*.[c|h]' | xargs grep -n your_pattern

ack does it even easier.

ack --type=cc your_pattern

Simply take your list of patterns from your file and "or" them together.

ack --type=cc 'foo|bar|baz'

This has the benefit of only search the files once, and not once for each pattern being searched for as you're doing.

Schwern
+1  A: 

I still think you should just use ack, but your code needed some serious love.

Here is an improved version of your program. It now takes the directory to search and patterns on the command line rather than having to ask for (and the user write) files. It searches all the files under the directory, not just the ones in the directory, using File::Find. It does this in one pass by concatenating all the patterns into regular expressions. It uses regexes instead of index() and substr() and reverse() and oh god. It simply uses built in filehandles rather than the FileHandle module and checking for eof(). Everything is declared lexical (my) instead of global (our). Strict and warnings are on for easier debugging.

#!/usr/bin/perl

use strict;
use warnings;
use File::Find;

die "Usage: search_directory function ...\n" unless @ARGV >= 2;

my $Search_Dir = shift;
my $Pattern = build_pattern(@ARGV);

find(
    {
        wanted => sub {
            return unless $File::Find::name =~ m/\.(c|h)$/i;
            find_func($File::Find::name, $pattern);
        },
        no_chdir => 1,
    },
    $Search_Dir
);


# Join all the function names into one pattern
sub build_pattern {
    my @patterns;
    for my $name (@_) {
        # Turn foo() into foo.  This replaces all that reverse() and rindex()
        # and substr() stuff.
        $name =~ s{\(.*}{};

        # Use \Q to protect against regex metacharacters in the input
        push @patterns, qr{\Q$name\E};
    }

    # Join them up into one pattern.
    return join "|", @patterns;
}


sub find_func {
    my( $file, $pattern ) = @_;

    open(my $fh, "<", $file) or die "Can't open $file: $!";

    while (my $line = <$fh>) {
        # XXX not all functions are unindented, but your choice
        next if $line =~ m/^\s+/;

        print "$file:$.: $line" if $line =~ $pattern;
    }
}
Schwern
hi thanks for your response.... but am newbie to perl.. can u xplain me in detail... when i run ur code it gives me error... thanks alot again... my intension is that i have list of function names in a text file and i want to search those function names in all c files and h files that contain in a folder and display the line no and the entire line wer it is used... for eg... in the text file it can be lik foo() or void foo (abc) or foo alone also... so i have to take foo alone and search for the usage.. and also the line where it is called... thanks alot if u solve my prob..
lokesh
Nice clean code, but you've got an error. File::Find changes the working directory as it goes. If you modify the call to find_func (line 15) to be: findunc( $_, $Pattern ); then the code works.
daotoad
@daotoad Ahh, it worked for me because I was always feeding it an absolute path. Using $_ is not entirely correct because then it can't print the full path to the matched file. Instead I'll use no_chdir and $File::Find::name. Thanks.
Schwern
@lokesh A) your writing pains me and all who read it. You'll find you get a better response to write clearly without the "ur"'s and "u"'s and all the ...'s. B) I can't help you without knowing what the error is. First rule of a good bug report is to report the bug! What was the error? C) I understand what you want and the code above does it. I leave it as an exercise for you to figure out how it does it, I'm not going to spoon feed you. If you have a specific question you'll just have to ask.
Schwern