#!/usr/bin/perl use feature 'state'; use strict; use warnings; use autodie; use threads; use threads::shared; use POSIX qw(mkfifo); use Carp qw(croak carp confess); use File::Spec; use Getopt::Std; use File::Touch; sub look_for_string{ my ($target,$file) = @_; my @target_chars = split(//,$target); open my $fh, '<',$file; my $readchars; my $line = 0; while(read($fh,$readchars,512)!= 0){ my @chars = split(//,$readchars);#this is a prototype for a c program, so lets if($chars[$#chars] ne "\n" && $chars[$#chars] ne " " && $chars[$#chars] ne "\0"){ my $i = 1; my $curpos = tell($fh); while($chars[$#chars-$i] ne "\n" && $chars[$#chars-$i] ne " " && $chars[$#chars-$i] ne "\0"){ $i++; } seek($fh,$curpos,$i);#if word is cutoff we rewind } NEXT_CHAR:for my $i ($#target_chars .. $#chars){ if($chars[$i] eq "\n"){ $line++; } if($chars[$i] eq "$target_chars[$#target_chars]"){ for my $j ( 1 .. $#target_chars){ if($target_chars[$#target_chars-$j] ne "$chars[$i-$j]"){ next NEXT_CHAR; } } close $fh; return $line;#return line number where found } } } close $fh; return undef; } sub printattr{ my $file = shift; my @fileattr = split(//,(stat($file))[2]); my @printable = qw( --- --x -w- -wx r-- r-x rw- rwx ); print "["; for my $attr (@fileattr){ print $printable[$attr % 8]; } print "]\n"; } sub explore{ my ($dir,$lock,$backpipe,$backlock,$target,$text_target) = @_; my @files = glob "$dir/*"; my $found=undef; foreach my $file (@files){ if(-d $file){ if($file =~ m/\.txt\z/){ if(defined $target && $file=~m/$target/gxms){ print $file." "; printattr($file); } } lock($backlock); open my $fh,'>',$backpipe; print $fh "$file\n"; close $fh; } elsif(defined $target){ if($file eq $dir."/".$target){ if(defined $text_target && $file =~ m/\.txt\z/ && ! -e "$file.lck"){#check if file already opened by other thread #in case some symlink sent it here lock($lock); touch("$file.lck"); $found = look_for_string($text_target,$file); unlink("$file.lck"); } lock($lock); if(defined $found){ print "found $text_target at line $found of file:$file "; printattr($file); } } } else{ if(defined $text_target && $file =~ m/\.txt\z/ && ! -e "$file.lck"){ touch("$file.lck"); $found = look_for_string($text_target,$file); unlink("$file.lck"); } lock($lock); if(defined $found){ print "found $text_target at line $found of file:$file "; printattr($file); } } } threads->exit(); } sub nb_running_threads{ my $threads = shift; my $res = 0; for my $th (@$threads){ $res += $th->is_running(); } return $res; } sub main{ my $args = shift; my $tmp= File::Spec->rel2abs($args->[1]); my $target = $args->[0]; my $max_threads = $args->[2]; my $text_target = $args->[3]; my %explored;#hash containing already explored paths if(!defined($max_threads)){ $max_threads = 8; } my $backpipe = "backpipe"; if(-e $backpipe){ unlink $backpipe; } mkfifo($backpipe,0700) or croak "could not make backpipe: $!"; $backpipe = File::Spec->rel2abs($backpipe); my $lock :shared; my $backlock :shared; my @thread_ids; $thread_ids[0] = threads->create('explore',$tmp,\$lock,$backpipe,\$backlock,$target,$text_target);#now the #first exploring thread to get the pump primed my $thc = 1; open my $fhbk, '<',$backpipe; while(nb_running_threads(\@thread_ids)){ while(<$fhbk>){ chomp($_); my $tmp_c; if($thc == $max_threads -1 ){ print "waiting for thread to die\n"; while(!defined($tmp_c)){ for my $i (0 .. $#thread_ids){ if(! $thread_ids[$i]->is_running()){ $thread_ids[$i]->join(); $tmp_c = $i; last; } } } } if(defined($tmp_c) && !defined $explored{$_}){ $explored{$_} = 1; $thread_ids[$tmp_c] = threads->create('explore',$_,\$lock,File::Spec->rel2abs($backpipe),\$backlock,$target,$text_target); } elsif(!defined $explored{$_}){ $explored{$_} = 1; $thread_ids[$thc] = threads->create('explore',$_,\$lock,File::Spec->rel2abs($backpipe),\$backlock,$target,$text_target); $thc++; } } } #lets close shop for my $th (@thread_ids){ $th->join(); } #cleanup unlink $backpipe; } our ($opt_f,#filename switch $opt_d,#dir switch $opt_t,#thread number switch $opt_m);#target text switch getopts('f:d:t:m:'); my @shortargs= ($opt_f,$opt_d,$opt_t,$opt_m); if(!defined($shortargs[1]) || (!defined $shortargs[0] && !defined $shortargs[3])){ croak <<"END" use me -f filename -d topdir -t max_thread_number and -m target_text END } main(\@shortargs);