pastebin - collaborative debugging tool
kpaste.net RSS


md5db script (threaded, ram-only version) with flac and large file support
Posted by Anonymous on Fri 21st Sep 2012 19:41
raw | new post
view followups (newest first): md5db script (threaded, ram-only version) with flac and large file support by Anonymous
modification of post by Anonymous (view diff)

  1. #!/usr/bin/perl
  2.  
  3. use 5.12.3;
  4. use strict;
  5. use warnings;
  6. use Cwd qw(abs_path cwd);
  7. use Digest::MD5 qw(md5_hex);
  8. use IO::Handle qw(autoflush);
  9. use File::Basename qw(basename dirname);
  10. #use File::Slurp qw(read_file);
  11. use diagnostics;
  12.  
  13. use threads qw(yield);
  14. use threads::shared;
  15. use Thread::Queue;
  16. use Thread::Semaphore;
  17. #use Fcntl qw(:flock);
  18. use POSIX qw(SIGINT);
  19. use POSIX qw(ceil);
  20.  
  21. chomp(my $cores = `grep -c ^processor /proc/cpuinfo`);
  22.  
  23. my (@lib, $mode);
  24.  
  25. # Path to and name of log file to be used for logging.
  26. my $logf = "$ENV{HOME}/md5db.log";
  27.  
  28. # Delimiter used for database
  29. my $delim = "\t\*\t";
  30.  
  31. # Array for storing the actual arguments used by the script internally.
  32. # Might be useful for debugging.
  33. my @cmd = (basename($0));
  34.  
  35. # Name of database file.
  36. my $db = 'md5.db';
  37.  
  38. # Clear screen command.
  39. my $clear = `clear && echo`;
  40.  
  41. # Creating a hash that will store the names of files that are
  42. # too big to fit into RAM. We'll process them last.
  43. my %large;
  44.  
  45. # Creating a few shared variables.
  46. # %err will be used for errors
  47. # $n will be used to count the number of files processed
  48. # %md5h is the database hash
  49. my %err :shared;
  50. my $n :shared = 0;
  51. my %md5h :shared;
  52. my %file_contents :shared;
  53. my $stopping :shared = 0;
  54. my $file_stack :shared = 0;
  55. my $busy :shared = 0;
  56. my @parts :shared = (0) x 2;
  57.  
  58. my $disk_size = 500000000;
  59.  
  60. # This will be used to control access to the logger subroutine.
  61. my $semaphore = Thread::Semaphore->new();
  62.  
  63. POSIX::sigaction(SIGINT, POSIX::SigAction->new(\&handler))
  64. || die "Error setting SIGINT handler: $!\n";
  65.  
  66. # Creating a custom POSIX signal handler.
  67. # First we create a shared variable that will work as a SIGINT switch.
  68. # Then we define the handler subroutine.
  69. # If ^C is pressed for the first time,
  70. # trip the switch and let the current threads finish.
  71. # If ^C is pressed a second time, quit directly.
  72. # Each subroutine to be used for starting threads will have to
  73. # take notice of the state of the $saw_sigint variable.
  74. my $saw_sigint :shared = 0;
  75. sub handler {
  76.         if ($saw_sigint == 1) {
  77.                 logger('int', $n);
  78.                 hash2file();
  79.                 exit;
  80.         } else { $saw_sigint = 1; }
  81. }
  82.  
  83. # Open file handle for the log file
  84. open(my $LOG, '>>', $logf) or die "Can't open '$logf': $!";
  85.  
  86. # Make the $LOG file handle unbuffered for instant logging.
  87. $LOG->autoflush(1);
  88.  
  89. # Duplicate STDERR as a regular file handle
  90. open(my $SE, ">&STDERR") or die "Can't duplicate STDERR: $!";
  91.  
  92. ### Subroutine for printing usage instructions
  93. sub usage {
  94.  
  95.         my $s = basename($0);
  96.  
  97.         say <<"HELP"
  98. Usage: $s [options] [directory 1] .. [directory N]
  99.  
  100.         -help Print this help message.
  101.  
  102.         -import Import MD5 sums to the database from already existing
  103.         \*.MD5 files in each directory.
  104.  
  105.         -index Index new files in each directory.
  106.  
  107.         -test Test the MD5 sums of the files in the database to see if
  108.         they've changed.
  109.  
  110. HELP
  111. }
  112.  
  113. # This loop goes through the argument list as passed to the script
  114. # by the user when ran.
  115. foreach my $arg (@ARGV) {
  116.  
  117.         # If argument starts with a dash '-', interprete it as an option
  118.         if ($arg =~ /^-/) {
  119.  
  120.                 given ($arg) {
  121.  
  122.                         # When '-import', set script mode to 'import', and call
  123.                         # the md5import subroutine later.
  124.                         when (/^-import$/) {
  125.                                 if (!$mode) { push(@cmd, $arg); $mode = 'import'; }
  126.                         }
  127.  
  128.                         # When '-help', set script mode to 'help', and print
  129.                         # usage instructions later.
  130.                         when (/^-help$/) {
  131.                                 if (!$mode) { push(@cmd, $arg); $mode = 'help'; }
  132.                         }
  133.  
  134.                         # When '-index', set script mode to 'index', and call
  135.                         # the md5index subroutine later.
  136.                         when (/^-index$/) {
  137.                                 if (!$mode) { push(@cmd, $arg); $mode = 'index'; }
  138.                         }
  139.  
  140.                         # When '-test', set the script mode to 'test', and call
  141.                         # the md5test subroutine later.
  142.                         when (/^-test$/) {
  143.                                 if (!$mode) { push(@cmd, $arg); $mode = 'test'; }
  144.                         }
  145.                 }
  146.         # If argument is a directory, include it in the @lib array
  147.         } elsif (-d $arg) { push(@lib, $arg); push(@cmd, $arg); }
  148. }
  149.  
  150. # If no switches were used, print usage instructions
  151. if (!@lib ||
  152.         !$mode ||
  153.         $mode eq 'help')
  154.         { usage; exit; }
  155.  
  156. #say "@cmd\n";
  157.  
  158. # Subroutine for controlling the log file
  159. # Applying a semaphore so multiple threads won't try to
  160. # access it at once, just in case ;-)
  161. sub logger {
  162.  
  163.         $semaphore->down();
  164.  
  165.         my($arg, $sw, @fn, $n);
  166.  
  167.         # Creating a variable to hold the current time.
  168.         my $now = localtime(time);
  169.  
  170.         # Array of accepted switches to this subroutine
  171.         my @larg = qw{start int gone corr diff end};
  172.  
  173.         # Loop through all the arguments passed to this subroutine
  174.         # Perform checks that decide which variable the arguments are to
  175.         # be assigned to.
  176.         CHECK: while (@_) {
  177.                         $arg = shift(@_);
  178.  
  179.                         # If $arg is a switch, set the $sw variable and start
  180.                         # the next iteration of the CHECK loop.
  181.                         foreach (@larg) {
  182.                                 if ($_ eq $arg) { $sw = $arg; next CHECK; }
  183.                         }
  184.  
  185.                         # If $arg is a number assign it to $n, if it's a file
  186.                         # add it to @fn.
  187.                         if ($arg =~ /^[0-9]*$/) { $n = $arg; }
  188.                         else { push(@fn, $arg); }
  189.         }
  190.         given ($sw) {
  191.                 # Starts writing the log.
  192.                 when ('start') {
  193.                         say $LOG "\n**** Logging started on $now ****\n";
  194.                         say $LOG "Running script in '$mode' mode.\n";
  195.                 }
  196.                 # When the script is interrupted by user pressing ^C,
  197.                 # say so in STDOUT, close the log.
  198.                 when ('int') {
  199.                         say "\nInterrupted by user!\n";
  200.                         say $LOG $n . " file(s) were tested.";
  201.                         say $LOG "\n**** Logging ended on $now ****\n";
  202.                         close $LOG or die "Can't close '$LOG': $!";
  203.                 }
  204.                 # Called when file has been deleted or moved.
  205.                 when ('gone') {
  206.                         say $LOG $fn[0] . "\n\t" . "has been (re)moved.\n";
  207.                         $err{$fn[0]} = "has been (re)moved.\n";
  208.                 }
  209.                 # Called when file has been corrupted.
  210.                 when ('corr') {
  211.                         say $LOG $fn[0] . "\n\t" .
  212.                         "has been corrupted.\n";
  213.                         $err{$fn[0]} = "has been corrupted.\n";
  214.                 }
  215.                 when ('diff') {
  216.                         say $LOG $fn[0] . "\n\t" .
  217.                                 "doesn't match the hash in database.\n";
  218.                         $err{$fn[0]} = "doesn't match the hash in database.\n";
  219.                 }
  220.                 # Called when done, and to close the log.
  221.                 # If no errors occurred write "Everything is OK!" to the log.
  222.                 # If errors occurred print the %err hash.
  223.                 # Either way, print number of files processed.
  224.                 when ('end') {
  225.                         if (!%err) {
  226.                                 say $LOG "\nEverything is OK!\n";
  227.                         } else {
  228.                                 say "\n**** Errors Occurred ****\n";
  229.                                 foreach my $fn (sort keys %err) {
  230.                                         say $SE $fn . "\n\t" . $err{$fn};
  231.                                 }
  232.                         }
  233.  
  234.                         say $LOG $n . " file(s) were tested.\n" if ($n);
  235.                         say $LOG "\n**** Logging ended on $now ****\n";
  236.                         close $LOG or die "Can't close '$LOG': $!";
  237.                 }
  238.         }
  239.         $semaphore->up();
  240. }
  241.  
  242.  
  243. # Subroutine for reading a database file into the database hash.
  244. # This is the first subroutine that will be executed and all others
  245. # depend upon it, cause without it we don't have a
  246. # database hash to work with.
  247. sub file2hash {
  248.  
  249.         # The format string which is used for parsing the database file
  250.         my $format = qr/^.*\t\*\t[[:alnum:]]{32}$/;
  251.         my (@dbfile, @gone);
  252.  
  253.         # Open the database file and read it into the @dbfile variable
  254.         open(MD5DB, '<', $db) or die "Can't open '$db': $!";
  255.         chomp (@dbfile = (<MD5DB>));
  256.         close(MD5DB) or die "Can't close '$db': $!";
  257.  
  258.         # Loop through all the lines in the database file and split
  259.         # them before storing in the database hash.
  260.         # Also, print each line to STDOUT for debug purposes
  261.         foreach my $line (@dbfile) {
  262.  
  263.                 # If current line matches the proper database file format,
  264.                 # continue.
  265.                 if ($line =~ /$format/) {
  266.  
  267.                         my ($fn, $hash) = (split(/\Q$delim/, $line));
  268.  
  269.                         # If $fn is a real file and not already in the hash,
  270.                         # continue.
  271.                         if (-f $fn && ! $md5h{$fn}) {
  272.                                         $md5h{$fn} = $hash;
  273.                                         say "$fn". "$delim" . "$hash";
  274.  
  275.                         # Saves the names of deleted or moved files in '@gone'
  276.                         # for printing at the end of this subroutine.
  277.                         } else { push(@gone, $fn); }
  278.                 }
  279.         }
  280.  
  281.         # Clears the screen, thereby scrolling past the database file print
  282.         print $clear;
  283.  
  284.         # Loops through the @gone array and logs every file name
  285.         # that's been deleted or moved.
  286.         foreach my $fn (@gone) { logger('gone', $fn); }
  287. }
  288.  
  289. # Subroutine for printing the database hash to the database file
  290. sub hash2file {
  291.  
  292.         open(MD5DB, '>', "$db") or die "Can't open '$db': $!";
  293.         # Loops through all the keys in the database hash and prints
  294.         # the entries (divided by the $delim variable) to the database file.
  295.         foreach my $k (sort keys %md5h) {
  296.                 say MD5DB $k . $delim . $md5h{$k};
  297.         }
  298.         close(MD5DB) or die "Can't close '$db': $!";
  299. }
  300.  
  301. # Subroutine for finding files
  302. # Finds all the files inside the directory name passed to it,
  303. # and sorts the output before storing it in the @files array.
  304. sub getfiles {
  305.  
  306.         my $dn = shift;
  307.         my @files;
  308.  
  309.         open(FIND, '-|', qq(find -L "$dn" -type f -name "*"))
  310.         or die "Can\'t run 'find': $!";
  311.         while (my $fn = (<FIND>)) {
  312.                         chomp($fn);
  313.                         $fn =~ s($dn/)();
  314.                         push(@files, $fn) if (-f $fn && $fn ne basename($db));
  315.         }
  316.         close(FIND) or die "Can't close 'find': $!";
  317.         return @files;
  318. }
  319.  
  320. # Subroutine for finding and parsing *.MD5 files, adding the hashes
  321. # to the database hash and thereby also to the file.
  322. # It takes 1 argument:
  323. # (1) the thread queue
  324. sub md5import {
  325.         my $q = shift;
  326.  
  327.         my ($fn, $hash, @fields, @lines);
  328.  
  329.         # The format string which is used for parsing the *.MD5 files.
  330.         my $format = '^[[:alnum:]]{32}\s\*.*';
  331.  
  332.         # Loop through the @files array.
  333.         while (my $md5fn = $q->dequeue_nb()) {
  334.  
  335.                 # If the file extension is *.MD5 in either upper- or
  336.                 # lowercase, continue.
  337.                 if ($md5fn =~ /.md5$/i) {
  338.  
  339.                         # Open the *.MD5 file and read its contents to the
  340.                         # @lines array.
  341.                         open(MD5, '<', $md5fn) or die "Can't open '$md5fn': $!";
  342.                         chomp(@lines = (<MD5>));
  343.                         close(MD5) or die "Can't close '$md5fn': $!";
  344.  
  345.                         # Loop to check that the format of the *.MD5 file really
  346.                         # is correct before proceeding.
  347.                         foreach my $line (@lines) {
  348.  
  349.                                 # If format string matches the line(s) in the *.MD5
  350.                                 # file, continue.
  351.                                 if ($line =~ /$format/) {
  352.  
  353.                                         # Split the line so that the hash and file name go
  354.                                         # into @fields array.
  355.                                         # After that strip the path (if any) of the file
  356.                                         # name, and prepend the path of the *.MD5 file to
  357.                                         # it instead.
  358.                                         # Store hash and file name in the $hash and $fn
  359.                                         # variables for readability.
  360.                                         @fields = split(/\s\Q*/, $line, 2);
  361.                                         my $path = dirname($md5fn);
  362.                                         $hash = $fields[0];
  363.  
  364.                                         if ($path eq '.') { $fn = basename($fields[1]); }
  365.                                         else { $fn = dirname($md5fn)
  366.                                                                 . '/' . basename($fields[1]); }
  367.  
  368.                                         # Convert CR+LF newlines to proper LF to avoid
  369.                                         # identical file names from being interpreted as
  370.                                         # different.
  371.                                         $fn =~ s/\r//;
  372.  
  373.                                         # Unless file name already is in the database hash,
  374.                                         # print a message, add it to the hash.
  375.                                         if (! $md5h{$fn} && -f $fn) {
  376.  
  377.                                                 say "$fn" . "\n\t" .
  378.                                                 "Imported MD5 sum from '" .
  379.                                                 basename($md5fn) .
  380.                                                 "'.\n";
  381.  
  382.                                                 $md5h{$fn} = $hash;
  383.  
  384.                                         # If file name is not a real file, write to
  385.                                         # the log.
  386.                                         # If file name is in database hash but the
  387.                                         # MD5 sum from the MD5 file doesn't match,
  388.                                         # print to the log.
  389.                                         } elsif (! -f $fn) { logger('gone', $fn); }
  390.                                         elsif ($md5h{$fn} ne $hash)
  391.                                         { logger('diff', $md5fn); }
  392.                                 }
  393.                         }
  394.                 }
  395.         }
  396. }
  397.  
  398. sub md5sum {
  399.         my $fn = shift;
  400.         my $last = $parts[1];
  401.  
  402.         # If $last is set it means we're dealing with a split file, so
  403.         # set $busy to 1 for the other threads to fall asleep. We need to
  404.         # stay in this thread when we're splitting files. So we create an
  405.         # 'until' loop which will process all the parts until the last one.
  406.         if ($last) {
  407.                 $busy = 1;
  408.                 my $md5 = Digest::MD5->new;
  409.                 my $done = 0;
  410.                 until ($done == $last) {
  411.                         if ($file_contents{$fn}) {
  412.                                 $md5->add($file_contents{$fn});
  413.                                 lock($file_stack);
  414.                                 $file_stack -= length($file_contents{$fn});
  415.                                 lock(%file_contents);
  416.                                 delete($file_contents{$fn});
  417.                                 $done++;
  418.                         }
  419.                                 yield();
  420.                 }
  421.                 my $hash = $md5->hexdigest;
  422.                 $busy = 0;
  423.                 return $hash;
  424.         } else {
  425.                 my $hash = md5_hex($file_contents{$fn});
  426.                 lock($file_stack);
  427.                 $file_stack -= length($file_contents{$fn});
  428.                 lock(%file_contents);
  429.                 delete($file_contents{$fn});
  430.                 return $hash;
  431.         }
  432. }
  433.  
  434. # Subroutine to index the files
  435. # i.e calculate and store the MD5 sums in the database hash/file.
  436. # It takes 1 argument:
  437. # (1) the thread queue
  438. sub md5index {
  439.         my $q = shift;
  440.         my $tid = threads->tid();
  441.  
  442.         # Loop through the thread que.
  443.         LOOP2: while ((my $fn = $q->dequeue_nb()) || !$stopping) {
  444.                         if ($fn) {
  445.                                 $md5h{$fn} = md5sum($fn);
  446.                                 lock($n);
  447.                                 $n++;
  448.                         } else {
  449.                                 yield();
  450.                         }
  451.  
  452.                         # If the $saw_sigint variable has been tripped.
  453.                         # Quit this 'while' loop, thereby closing the thread.
  454.                         if ($saw_sigint == 1) {
  455.                                 say "Closing thread: " . $tid;
  456.                                 last;
  457.                         }
  458.         }
  459.  
  460.         while (!$q->pending() && !$stopping) {
  461.                 yield();
  462.                 goto(LOOP2) if (%large && !$busy);
  463.         }
  464. }
  465.  
  466. # Subroutine for testing to see if the MD5 sums
  467. # in the database file are correct (i.e. have changed or not).
  468. # It takes 1 argument:
  469. # (1) the thread queue
  470. sub md5test {
  471.         my $q = shift;
  472.         my $tid = threads->tid();
  473.         my ($oldmd5, $newmd5);
  474.  
  475.         # Loop through the thread queue.
  476.         LOOP: while ((my $fn = $q->dequeue_nb()) || !$stopping) {
  477.                                 if ($fn) {
  478.                                         $newmd5 = md5sum($fn);
  479.                                         $oldmd5 = $md5h{$fn};
  480.  
  481.                                         # If the new MD5 sum doesn't match the one in the hash,
  482.                                         # and file doesn't already exist in the %err hash,
  483.                                         # log it and replace the old MD5 sum in the hash with
  484.                                         # the new one.
  485.                                         if ($newmd5 ne $oldmd5 && ! $err{$fn}) {
  486.                                                 logger('diff', $fn);
  487.                                                 $md5h{$fn} = $newmd5;
  488.                                         }
  489.                                         lock($n);
  490.                                         $n++;
  491.                                 } else {
  492.                                         yield();
  493.                                 }
  494.                                 # If the $saw_sigint variable has been tripped.
  495.                                 # Quit this 'while' loop, thereby closing the thread.
  496.                                 if ($saw_sigint == 1) {
  497.                                         say "Closing thread: " . $tid;
  498.                                         last;
  499.                                 }
  500.         }
  501.  
  502.         while (!$q->pending() && !$stopping) {
  503.                 yield();
  504.                 goto(LOOP) if (%large && !$busy);
  505.         }
  506. }
  507.  
  508. sub md5flac {
  509.         my $fn = shift;
  510.         my (@req, $hash);
  511.  
  512.         if ($fn =~ /.flac$/i) {
  513.  
  514.                 if (! @req) {
  515.                         chomp(@req = ( `which flac metaflac 2>&-` ));
  516.  
  517.                         if (! $req[0] || ! $req[1]) {
  518.                                 say "You need both 'flac' and 'metaflac' to test FLAC files!\n" .
  519.                                 "Using normal test method...\n";
  520.                                 @req = '0';
  521.                                 return;
  522.                         }
  523.                 }
  524.  
  525.                 unless ($req[0] = '0') {
  526.                         chomp($hash = `metaflac --show-md5sum "$fn" 2>&-`);
  527.                         if ($? != 0 && $? != 2) { logger('corr', $fn); return; }
  528.  
  529.                         system('flac', '--totally-silent', '--test', "$fn");
  530.                         if ($? != 0 && $? != 2) { logger('corr', $fn); return; }
  531.  
  532.                         return $hash;
  533.                 }
  534.         }
  535. }
  536.  
  537. # Create the thread queue.
  538. my $q = Thread::Queue->new();
  539.  
  540. # Depending on which script mode is active,
  541. # set the @run array to the correct arguments.
  542. # This will be used to start the threads later.
  543. my @run;
  544. given ($mode) {
  545.         when ('import') {
  546.                 @run = (\&md5import, $q);
  547.         }
  548.         when ('index') {
  549.                 @run = (\&md5index, $q);
  550.         }
  551.         when ('test') {
  552.                 @run = (\&md5test, $q);
  553.         }
  554. }
  555.  
  556. my @threads;
  557. foreach (1 .. $cores) {
  558.         push(@threads, threads->create(@run));
  559. }
  560.  
  561. # This loop is where the actual action takes place
  562. # (i.e. where all the subroutines get called from)
  563. foreach my $dn (@lib) {
  564.         if (-d $dn) {
  565.                 # Changing $dn to the absolute path.
  566.                 $dn = abs_path($dn);
  567.  
  568.                 # Adding the current PATH to the $db variable.
  569.                 $db = "$dn/$db";
  570.  
  571.                 # Change directory to $dn.
  572.                 chdir($dn)
  573.                         or die "Can't change directory to '$dn': $!";
  574.  
  575.                 # Start logging.
  576.                 logger('start');
  577.  
  578.                 # If the database file is a real file,
  579.                 # store it in the database hash.
  580.                 file2hash() if (-f $db);
  581.  
  582.                 given ($mode) {
  583.                         when ('import') {
  584.                                 #say("Enqueueing files");
  585.                                 # Get all the file names and put them in the queue.
  586.                                 foreach my $fn (sort(keys(%md5h))) { $q->enqueue($fn); }
  587.                         }
  588.                         when ('index') {
  589.                                 #say("Enqueueing files");
  590.                                 # Get all the file names and put them in the queue.
  591.                                 foreach my $fn (getfiles($dn)) {
  592.                                         while ($file_stack >= $disk_size) {
  593.                                                 say("$file_stack > $disk_size");
  594.                                                 yield();
  595.                                         }
  596.                                         # Unless file name exists in the database hash,
  597.                                         # continue.
  598.                                         unless ($md5h{$fn}) {
  599.                                                 my $size = (stat($fn))[7];
  600.                                                 if ($size < $disk_size) {
  601.  
  602.                                                         say("${fn}: reading");
  603.                                                         open(FILE, '<:raw', $fn) or die "Can't open '$fn': $!";
  604.                                                         read(FILE, $file_contents{$fn}, $size);
  605.                                                         close(FILE) or die "Can't close '$fn': $!";
  606.  
  607.                                                         say("${fn}: queueing");
  608.                                                         lock($file_stack);
  609.                                                         $file_stack += length($file_contents{$fn});
  610.                                                         $q->enqueue($fn);
  611.                                                 } else {
  612.                                                         say("${fn}: $size > $disk_size");
  613.                                                         $large{$fn} = 1;
  614.                                                 }
  615.                                         }
  616.                                 }
  617.                         }
  618.                         when ('test') {
  619.                                 #say("Enqueueing files");
  620.                                 # Fetch all the keys for the database hash and put
  621.                                 # them in the queue.
  622.                                 foreach my $fn (sort(keys(%md5h))) {
  623.                                         while ($file_stack >= $disk_size) {
  624.                                                 say("$file_stack > $disk_size");
  625.                                                 yield();
  626.                                         }
  627.  
  628.                                         my $size = (stat($fn))[7];
  629.                                         if ($size < $disk_size) {
  630.  
  631.                                                 say("${fn}: reading");
  632.                                                 open(FILE, '<:raw', $fn) or die "Can't open '$fn': $!";
  633.                                                 read(FILE, $file_contents{$fn}, $size);
  634.                                                 close(FILE) or die "Can't close '$fn': $!";
  635.  
  636.                                                 say("${fn}: queueing");
  637.                                                 lock($file_stack);
  638.                                                 $file_stack += length($file_contents{$fn});
  639.                                                 $q->enqueue($fn);
  640.                                         } else {
  641.                                                 say("${fn}: $size > $disk_size");
  642.                                                 $large{$fn} = 1;
  643.                                         }
  644.                                 }
  645.                         }
  646.                 }
  647.  
  648.                 # This block is run after all the files that fit in RAM are
  649.                 # done processing. This deals with the large files that
  650.                 # don't fit in RAM.
  651.                 if (%large) {
  652.  
  653.                         while ($file_stack > 0) {
  654.                                 say "File stack is: $file_stack"; yield(); }
  655.                         foreach my $fn (sort(keys(%large))) {
  656.                                 $parts[1] = ceil((stat($fn))[7] / $disk_size);
  657.                                 my $last = $parts[1];
  658.  
  659.                                 open(FILE, '<:raw', $fn) or die "Can't open '$fn': $!";
  660.                                 foreach my $n (1 .. $last) {
  661.                                         while ($file_stack > 0) { yield(); }
  662.                                         say "${fn}\nPart $n of $parts[1] done ..";
  663.                                         if ($n == 1) {
  664.                                                 sysread(FILE, $file_contents{$fn}, $disk_size);
  665.  
  666.                                                 lock($file_stack);
  667.                                                 $file_stack += length($file_contents{$fn});
  668.                                                 $parts[0] = $n;
  669.                                                 $q->enqueue($fn);
  670.                                         } elsif ($n == $last) {
  671.                                                 sysread(FILE, $file_contents{$fn}, $disk_size);
  672.  
  673.                                                 lock($file_stack);
  674.                                                 $file_stack += length($file_contents{$fn});
  675.                                                 $parts[0] = $n;
  676.                                         } else {
  677.                                                 sysread(FILE, $file_contents{$fn}, $disk_size);
  678.  
  679.                                                 lock($file_stack);
  680.                                                 $file_stack += length($file_contents{$fn});
  681.                                                 $parts[0] = $n;
  682.                                         }
  683.                                 }
  684.                                 close(FILE) or die "Can't close '$fn': $!";
  685.                                 foreach (@parts) { $_ = 0; }
  686.                         }
  687.                 }
  688.  
  689. #     use Digest::MD5;
  690. #    $md5 = Digest::MD5->new;
  691. #    $md5->add('foo', 'bar');
  692. #    $md5->add('baz');
  693. #    $digest = $md5->hexdigest;
  694. #    print "Digest is $digest\n";
  695.  
  696.  
  697.                 $stopping = 1;
  698.                 foreach my $t (threads->list()) { $t->join(); }
  699.                 #say("All threads joined");
  700.  
  701.                 # Print the hash to the database file and close the log
  702.                 hash2file();
  703.                 logger('end', $n);
  704.         }
  705. }

Submit a correction or amendment below (click here to make a fresh posting)
After submitting an amendment, you'll be able to view the differences between the old and new posts easily.

Syntax highlighting:

To highlight particular lines, prefix each line with {%HIGHLIGHT}




All content is user-submitted.
The administrators of this site (kpaste.net) are not responsible for their content.
Abuse reports should be emailed to us at