pastebin - collaborative debugging tool
kpaste.net RSS

Difference between
modified post a63 by Anonymous on Mon 15th Oct 2012 06:13
original post da393 by Anonymous on Thu 11th Oct 2012 04:48
Download diff
Show old version | new version | both versions

    
11
#!/usr/bin/perl
22
33
use 5.14.2;
44
use strict;
55
use warnings;
66
use Cwd qw(abs_path cwd);
77
use Digest::MD5 qw(md5_hex);
88
use IO::Handle qw(autoflush);
99
use File::Basename qw(basename dirname);
1010
#use File::Slurp qw(read_file);
1111
use diagnostics;
1212
1313
use threads qw(yield);
1414
use threads::shared;
1515
use Thread::Queue;
1616
use Thread::Semaphore;
1717
#use Fcntl qw(:flock);
1818
use POSIX qw(SIGINT);
1919
use POSIX qw(ceil);
2020
2121
# Create the thread queue.
2222
my $q = Thread::Queue->new();
2323
2424
chomp(my $cores = `grep -c ^processor /proc/cpuinfo`);
2525
2626
my (@lib, $mode);
2727
2828
# Path to and name of log file to be used for logging.
2929
my $logf = "$ENV{HOME}/md5db.log";
3030
3131
# Delimiter used for database
3232
my $delim = "\t\*\t";
3333
3434
# Array for storing the actual arguments used by the script internally.
3535
# Might be useful for debugging.
3636
my @cmd = (basename($0));
3737
3838
# Name of database file.
3939
my $db = 'md5.db';
4040
4141
# Clear screen command.
4242
my $clear = `clear && echo`;
4343
4444
# Creating a hash that will store the names of files that are
4545
# too big to fit into RAM. We'll process them last.
4646
my %large :shared;
4747
4848
# Creating a few shared variables.
4949
# %err will be used for errors
5050
# $n will be used to count the number of files processed
5151
# %md5h is the database hash
5252
my %err :shared;
5353
my $n :shared = 0;
5454
my %md5h :shared;
5555
my %file_contents :shared;
5656
my $stopping :shared = 0;
5757
my $file_stack :shared = 0;
5858
my $busy :shared = 0;
5959
6060
my $disk_size = 1000000000;
6161
6262
# This will be used to control access to the logger subroutine.
6363
my $semaphore = Thread::Semaphore->new();
6464
6565
POSIX::sigaction(SIGINT, POSIX::SigAction->new(\&handler))
6666
|| die "Error setting SIGINT handler: $!\n";
6767
6868
# Creating a custom POSIX signal handler.
6969
# First we create a shared variable that will work as a SIGINT switch.
7070
# Then we define the handler subroutine.
7171
# Each subroutine to be used for starting threads will have to
7272
# take notice of the state of the $saw_sigint variable.
7373
my $saw_sigint :shared = 0;
7474
sub handler { $saw_sigint = 1; }
7575
7676
# Open file handle for the log file
7777
open(my $LOG, '>>', $logf) or die "Can't open '$logf': $!";
7878
7979
# Make the $LOG file handle unbuffered for instant logging.
8080
$LOG->autoflush(1);
8181
8282
# Duplicate STDERR as a regular file handle
8383
open(my $SE, ">&STDERR") or die "Can't duplicate STDERR: $!";
8484
8585
### Subroutine for printing usage instructions
8686
sub usage {
8787
8888
	my $s = basename($0);
8989
9090
	say <<"HELP"
9191
Usage: $s [options] [directory 1] .. [directory N]
9292
9393
	-help Print this help message.
9494
9595
	-double Check database for files that have identical
9696
	hashes.
9797
9898
	-import Import MD5 sums to the database from already existing
9999
	\*.MD5 files in each directory.
100100
101101
	-index Index new files in each directory.
102102
103103
	-test Test the MD5 sums of the files in the database to see if
104104
	they've changed.
105105
106106
HELP
107107
}
108108
109109
# This loop goes through the argument list as passed to the script
110110
# by the user when ran.
111111
foreach my $arg (@ARGV) {
112112
113113
	# If argument starts with a dash '-', interprete it as an option
114114
	if ($arg =~ /^-/) {
115115
116116
		given ($arg) {
117117
			# When '-double', set script mode to 'double', and call
118118
			# the md5double subroutine later.
119119
			when (/^-double$/) {
120120
				if (!$mode) { push(@cmd, $arg); $mode = 'double'; }
121121
			}
122122
123123
			# When '-import', set script mode to 'import', and call
124124
			# the md5import subroutine later.
125125
			when (/^-import$/) {
126126
				if (!$mode) { push(@cmd, $arg); $mode = 'import'; }
127127
			}
128128
129129
			# When '-help', set script mode to 'help', and print
130130
			# usage instructions later.
131131
			when (/^-help$/) {
132132
				if (!$mode) { push(@cmd, $arg); $mode = 'help'; }
133133
			}
134134
135135
			# When '-index', set script mode to 'index', and call
136136
			# the md5index subroutine later.
137137
			when (/^-index$/) {
138138
				if (!$mode) { push(@cmd, $arg); $mode = 'index'; }
139139
			}
140140
141141
			# When '-test', set the script mode to 'test', and call
142142
			# the md5test subroutine later.
143143
			when (/^-test$/) {
144144
				if (!$mode) { push(@cmd, $arg); $mode = 'test'; }
145145
			}
146146
		}
147147
	# If argument is a directory, include it in the @lib array
148148
	} elsif (-d $arg) {
149149
		my $dn = abs_path($arg);
150150
		push(@lib, $dn); push(@cmd, $dn); }
151151
}
152152
153153
# If no switches were used, print usage instructions
154154
if (!@lib || !$mode || $mode eq 'help')
155-
	{ usage; exit; }
155+
	{ usage(); exit; }
156156
157157
#say "@cmd\n";
158158
159159
# This routine is called if something goes wrong and the script needs
160160
# to quit prematurely.
161161
sub iquit {
162162
	my $tid = threads->tid();
163163
	if ($tid == 1) {
164164
165165
		# Set the $stopping variable to let the threads know it's time
166166
		# to stop, and sleep for 1 second so they'll have time to quit.
167167
		{ lock($stopping);
168168
		$stopping = 1; }
169169
170170
		sleep(1);
171171
172172
		# Write the hash to the database file and write to the log.
173173
		hash2file();
174174
		logger('int', $n);
175175
176176
		# Detaching the threads so Perl will clean up after us.
177177
		foreach my $t (threads->list()) { $t->detach(); }
178178
		exit;
179179
180180
	# If the thread calling this function isn't thread 0/1, yield until
181181
	# $stopping is set.
182182
	} elsif ($tid > 1) { while (!$stopping) { yield(); } }
183183
}
184184
185185
# Subroutine for controlling the log file
186186
# Applying a semaphore so multiple threads won't try to
187187
# access it at once, just in case ;-)
188188
sub logger {
189189
190190
	$semaphore->down();
191191
192192
	my($arg, $sw, @fn, $n);
193193
194194
	# Creating a variable to hold the current time.
195195
	my $now = localtime(time);
196196
197197
	# Array of accepted switches to this subroutine
198198
	my @larg = qw{start int gone corr diff end};
199199
200200
	# Loop through all the arguments passed to this subroutine
201201
	# Perform checks that decide which variable the arguments are to
202202
	# be assigned to.
203203
	CHECK: while (@_) {
204204
			$arg = shift(@_);
205205
206206
			# If $arg is a switch, set the $sw variable and start
207207
			# the next iteration of the CHECK loop.
208208
			foreach (@larg) {
209209
				if ($_ eq $arg) { $sw = $arg; next CHECK; }
210210
			}
211211
212212
			# If $arg is a number assign it to $n, if it's a file
213213
			# add it to @fn.
214-
			if ($arg =~ /^[0-9]*$/) { $n = $arg; }
214+
			if ($arg =~ /^[0-9]+$/) { $n = $arg; }
215215
			else { push(@fn, $arg); }
216216
	}
217217
	given ($sw) {
218218
		# Starts writing the log.
219219
		when ('start') {
220220
			say $LOG "\n**** Logging started on $now ****\n";
221-
			say $LOG "Running script in '$mode' mode.\n";
221+
			say $LOG "Running script in '$mode' mode on:\n";
222+
			foreach my $dn (@lib) { say $LOG $dn; }
222224
		}
223225
		# When the script is interrupted by user pressing ^C,
224226
		# say so in STDOUT, close the log.
225227
		when ('int') {
226228
			say "\nInterrupted by user!\n";
227229
			say $LOG $n . " file(s) were tested.";
228230
			say $LOG "\n**** Logging ended on $now ****\n";
229231
			close $LOG or die "Can't close '$LOG': $!";
230232
		}
231233
		# Called when file has been deleted or moved.
232234
		when ('gone') {
233235
			say $LOG $fn[0] . "\n\t" . "has been (re)moved.\n";
234236
			$err{$fn[0]} = "has been (re)moved.\n";
235237
		}
236238
		# Called when file has been corrupted.
237239
		when ('corr') {
238240
			say $LOG $fn[0] . "\n\t" .
239241
			"has been corrupted.\n";
240242
			$err{$fn[0]} = "has been corrupted.\n";
241243
		}
242244
		when ('diff') {
243245
			say $LOG $fn[0] . "\n\t" .
244246
				"doesn't match the hash in database.\n";
245247
			$err{$fn[0]} = "doesn't match the hash in database.\n";
246248
		}
247249
		# Called when done, and to close the log.
248250
		# If no errors occurred write "Everything is OK!" to the log.
249251
		# If errors occurred print the %err hash.
250252
		# Either way, print number of files processed.
251253
		when ('end') {
252254
			if (!%err) {
253255
				say $LOG "\nEverything is OK!\n";
254256
			} else {
255257
				say "\n**** Errors Occurred ****\n";
256258
				foreach my $fn (sort keys %err) {
257259
					say $SE $fn . "\n\t" . $err{$fn};
258260
				}
259261
			}
260262
261263
			say $LOG $n . " file(s) were tested.\n" if ($n);
262264
			say $LOG "\n**** Logging ended on $now ****\n";
263265
			close $LOG or die "Can't close '$LOG': $!";
264266
		}
265267
	}
266268
	$semaphore->up();
267269
}
268270
269271
270272
# Subroutine for reading a database file into the database hash.
271273
# This is the first subroutine that will be executed and all others
272274
# depend upon it, cause without it we don't have a
273275
# database hash to work with.
274276
sub file2hash {
275277
276278
	my $db = shift;
277279
	my $dn = shift;
278280
279281
	# The format string which is used for parsing the database file
280282
	my $format = qr/^.*\t\*\t[[:alnum:]]{32}$/;
281-
	my (@dbfile, @gone);
283+
	my (@dbfile, @gone, $md5db_in);
282284
283285
	# Open the database file and read it into the @dbfile variable
284-
	open(MD5DB, '<', $db) or die "Can't open '$db': $!";
286+
	open($md5db_in, '<', $db) or die "Can't open '$db': $!";
285-
	chomp (@dbfile = (<MD5DB>));
287+
	chomp (@dbfile = (<$md5db_in>));
286-
	close(MD5DB) or die "Can't close '$db': $!";
288+
	close($md5db_in) or die "Can't close '$db': $!";
287289
288290
	# Loop through all the lines in the database file and split
289291
	# them before storing in the database hash.
290292
	# Also, print each line to STDOUT for debug purposes
291293
	foreach my $line (@dbfile) {
292294
293295
		# If current line matches the proper database file format,
294296
		# continue.
295297
		if ($line =~ /$format/) {
296298
297299
			# Split the line into relative file name, and MD5 sum.
298300
			# Also create another variable that contains the absolute
299301
			# file name.
300302
			my ($rel_fn, $hash) = (split(/\Q$delim/, $line));
301303
			my $abs_fn;
302304
			if ($dn ne '.') { $abs_fn = "$dn/$rel_fn"; }
303305
			else { $abs_fn = $rel_fn; }
304306
305-
			# If $rel_fn is a real file and not already in the hash,
307+
			# If $abs_fn is a real file and not already in the hash,
306308
			# continue.
307-
			if (-f $rel_fn && ! $md5h{$rel_fn}) {
309+
			if (-f $abs_fn && ! $md5h{$abs_fn}) {
312310
				$md5h{$abs_fn} = $hash;
313311
				say $abs_fn . $delim . $hash;
314312
315313
			# If the file is in the database hash but the MD5 sum
316314
			# found in the database doesn't match the one in the hash,
317315
			# print to the log.
318316
			#
319317
			# This will most likely only be the case for any extra
320318
			# databases that are found in the search path given to
321319
			# the script.
322320
			} elsif (-f $abs_fn && $md5h{$abs_fn} ne $hash) {
323321
				logger('diff', $abs_fn);
324322
			# Saves the names of deleted or moved files in '@gone'
325323
			# for printing at the end of this subroutine.
326324
			} elsif (! -f $abs_fn) { push(@gone, $abs_fn); }
327325
		}
328326
	}
329327
330328
	# Clears the screen, thereby scrolling past the database file print
331329
	print $clear;
332330
333331
	# Loops through the @gone array and logs every file name
334332
	# that's been deleted or moved.
335333
	foreach my $fn (@gone) { logger('gone', $fn); }
336334
}
337335
338336
# Subroutine for printing the database hash to the database file
339337
sub hash2file {
340338
341-
	open(MD5DB, '>', $db) or die "Can't open '$db': $!";
339+
	my $md5db_out;
340+
342342
	# Loops through all the keys in the database hash and prints
343343
	# the entries (divided by the $delim variable) to the database file.
344344
	foreach my $k (sort(keys(%md5h))) {
345-
		say MD5DB $k . $delim . $md5h{$k};
345+
		say $md5db_out $k . $delim . $md5h{$k};
346346
	}
347-
	close(MD5DB) or die "Can't close '$db': $!";
347+
	close($md5db_out) or die "Can't close '$db': $!";
348348
}
349349
350350
sub init_hash {
351351
	my $dn = shift;
352352
353353
	# Get all the file names in the path.
354354
	my($files, $md5dbs) = getfiles($dn);
355355
356356
	# But first import hashes from any databases found
357357
	# in the search path to avoid re-hashing them.
358358
	if (@{$md5dbs}) {
359359
		foreach my $db (@{$md5dbs}) {
360360
			my $dn = dirname($db);
361361
			file2hash($db, $dn);
362362
		}
363363
	}
364364
	return($files, $md5dbs);
365365
}
366366
367367
# Subroutine for finding files
368368
# Finds all the files inside the directory name passed to it,
369369
# and sorts the output before storing it in the @files array.
370370
sub getfiles {
371371
372372
	my $dn = shift;
373373
	my(@files, @md5dbs);
374374
375375
	open(FIND, '-|', 'find', $dn, '-type', 'f', '-name', '*', '-nowarn')
376376
	or die "Can't run 'find': $!";
377377
	while (my $fn = (<FIND>)) {
378378
			chomp($fn);
379379
			$fn =~ s($dn/)();
380380
			if (-f $fn && basename($fn) ne $db) {
381381
				push(@files, $fn);
382382
			} elsif (-f $fn && basename($fn) eq $db) {
383383
				push(@md5dbs, $fn);
384384
			}
385385
	}
386386
	close(FIND) or die "Can't close 'find': $!";
387387
	return(\@files, \@md5dbs);
388388
}
389389
390390
sub md5double {
391391
392392
	if (!keys(%md5h)) {
393393
		say "No database file. Run the script in 'index' mode first\n" .
394394
		"to index the files.";
395395
		exit;
396396
	}
397397
398398
	# Loop through the %md5h hash and save the checksums as keys in a
399399
	# new hash called %exists. Each of those keys will hold an
400400
	# anonymous array with the matching file names.
401401
	my %exists;
402402
	foreach my $fn (keys(%md5h)) {
403403
		my $hash = $md5h{$fn};
404404
		if (!$exists{${hash}}) {
405405
			$exists{${hash}}->[0] = $fn;
406406
		} else {
407407
			push(@{$exists{${hash}}}, $fn);
408408
		}
409409
	}
410410
411411
	# Loop through the %exists hash and print files that are identical,
412412
	# if any.
413413
	foreach my $hash (keys(%exists)) {
414414
		if (@{$exists{${hash}}} > 1) {
415415
			say "These files have the same hash (${hash}):";
416416
			foreach my $fn (@{$exists{${hash}}}) {
417417
				say $fn;
418418
			}
419419
			say "";
420420
		}
421421
	}
422422
}
423423
424424
# Subroutine for finding and parsing *.MD5 files, adding the hashes
425425
# to the database hash and thereby also to the file.
426426
# It takes 1 argument:
427427
# (1) file name
428428
sub md5import {
429429
430430
	my $md5fn = shift;
431431
432432
	my ($fn, $hash, @fields, @lines);
433433
434434
	# The format string which is used for parsing the *.MD5 files.
435435
	my $format = qr/^[[:alnum:]]{32}\s\*.*/;
436436
437437
	# If the file extension is *.MD5 in either upper- or
438438
	# lowercase, continue.
439439
	if ($md5fn =~ /.md5$/i) {
440440
441441
		# Open the *.MD5 file and read its contents to the
442442
		# @lines array.
443443
		open(MD5, '<', $md5fn) or die "Can't open '$md5fn': $!";
444444
		chomp(@lines = (<MD5>));
445445
		close(MD5) or die "Can't close '$md5fn': $!";
446446
447447
		# Loop to check that the format of the *.MD5 file really
448448
		# is correct before proceeding.
449449
		foreach my $line (@lines) {
450450
451451
			# If format string matches the line(s) in the *.MD5
452452
			# file, continue.
453453
			if ($line =~ /$format/) {
454454
455455
				# Split the line so that the hash and file name go
456456
				# into @fields array.
457457
				# After that strip the path (if any) of the file
458458
				# name, and prepend the path of the *.MD5 file to
459459
				# it instead.
460460
				# Store hash and file name in the $hash and $fn
461461
				# variables for readability.
462462
				@fields = split(/\s\Q*/, $line, 2);
463463
				my $path = dirname($md5fn);
464464
				$hash = $fields[0];
465465
466466
				if ($path eq '.') { $fn = basename($fields[1]); }
467467
				else { $fn = dirname($md5fn)
468468
				. '/' . basename($fields[1]); }
469469
470470
				# Convert CR+LF newlines to proper LF to avoid
471471
				# identical file names from being interpreted as
472472
				# different.
473473
				$fn =~ s/\r//;
474474
475475
				# Unless file name already is in the database hash,
476476
				# print a message, add it to the hash.
477477
				if (! $md5h{$fn} && -f $fn) {
478478
479479
					say "$fn" . "\n\t" .
480480
					"Imported MD5 sum from '" .
481481
					basename($md5fn) .
482482
					"'.\n";
483483
484484
					$md5h{$fn} = $hash;
485485
486486
					# If file name is not a real file, write to
487487
					# the log.
488488
					# If file name is in database hash but the
489489
					# MD5 sum from the MD5 file doesn't match,
490490
					# print to the log.
491491
				} elsif (! -f $fn) { logger('gone', $fn); }
492492
				elsif ($md5h{$fn} ne $hash)
493493
				{ logger('diff', $md5fn); }
494494
			}
495495
		}
496496
	}
497497
}
498498
499499
500500
sub md5sum {
501501
	my $fn = shift;
502502
	my $hash;
503503
504504
	while ($busy) { yield(); }
505505
506506
	if ($large{$fn}) {
507507
		lock($busy);
508508
		$busy = 1;
509-
		open(FILE, '<:raw', $fn) or die "Can't open '$fn': $!";
509+
		my $read_fn;
510-
		$hash = Digest::MD5->new->addfile("FILE")->hexdigest;
510+
		open($read_fn, '<:raw', $fn) or die "Can't open '$fn': $!";
511-
		close(FILE) or die "Can't close '$fn': $!";
511+
		$hash = Digest::MD5->new->addfile($read_fn)->hexdigest;
512+
		close($read_fn) or die "Can't close '$fn': $!";
512513
		$busy = 0;
513514
	} else {
514515
		$hash = md5_hex($file_contents{$fn});
515516
516517
		{ lock($file_stack);
517518
		$file_stack -= length($file_contents{$fn}); }
518519
519520
		{ lock(%file_contents);
520521
		delete($file_contents{$fn}); }
521522
	}
522523
523524
	return $hash;
524525
}
525526
526527
# Subroutine to index the files
527528
# i.e calculate and store the MD5 sums in the database hash/file.
528529
sub md5index {
529530
	my $tid = threads->tid();
530531
531532
	# Loop through the thread que.
532533
	LOOP2: while ((my $fn = $q->dequeue_nb()) || !$stopping) {
533534
			if (!$fn) { yield(); next; }
534535
535536
			$md5h{$fn} = md5sum($fn);
536537
			say "$tid $fn: done indexing (${file_stack})";
537538
538539
			{ lock($n);
539540
			$n++; }
540541
541542
			# If the $saw_sigint variable has been tripped.
542543
			# Quit this 'while' loop, thereby closing the thread.
543544
			if ($saw_sigint == 1) {
544545
				say "Closing thread: " . $tid;
545546
				iquit();
546547
			}
547548
	}
548549
549550
	while (!$q->pending() && !$stopping) {
550551
		yield();
551552
		goto(LOOP2);
552553
	}
553554
}
554555
555556
# Subroutine for testing to see if the MD5 sums
556557
# in the database file are correct (i.e. have changed or not).
557558
sub md5test {
558559
	my $tid = threads->tid();
559560
	my ($oldmd5, $newmd5);
560561
561562
	# Loop through the thread queue.
562563
	LOOP: while ((my $fn = $q->dequeue_nb()) || !$stopping) {
563564
				if (!$fn) { yield(); next; }
564565
565566
				$newmd5 = md5sum($fn);
566567
				say "$tid $fn: done testing (${file_stack})";
567568
				$oldmd5 = $md5h{$fn};
568569
569570
				# If the new MD5 sum doesn't match the one in the hash,
570571
				# and file doesn't already exist in the %err hash,
571572
				# log it and replace the old MD5 sum in the hash with
572573
				# the new one.
573574
				if ($newmd5 ne $oldmd5 && ! $err{$fn}) {
574575
					logger('diff', $fn);
575576
					$md5h{$fn} = $newmd5;
576577
				}
577578
578579
				{ lock($n);
579580
				$n++; }
580581
581582
				# If the $saw_sigint variable has been tripped.
582583
				# Quit this 'while' loop, thereby closing the thread.
583584
				if ($saw_sigint == 1) {
584585
					say "Closing thread: " . $tid;
585586
					iquit();
586587
				}
587588
	}
588589
589590
	while (!$q->pending() && !$stopping) {
590591
		yield();
591592
		goto(LOOP);
592593
	}
593594
}
594595
595596
sub md5flac {
596597
	my $fn = shift;
597598
	my (@req, $hash);
598599
599600
	if ($fn =~ /.flac$/i) {
600601
601602
		if (! @req) {
602603
			chomp(@req = ( `which flac metaflac 2>&-` ));
603604
604605
			if (! $req[0] || ! $req[1]) {
605606
				say "You need both 'flac' and 'metaflac' to test FLAC files!\n" .
606607
				"Using normal test method...\n";
607608
				@req = '0';
608609
				return;
609610
			}
610611
		}
611612
612613
		unless ($req[0] = '0') {
613614
			chomp($hash = `metaflac --show-md5sum "$fn" 2>&-`);
614615
			if ($? != 0 && $? != 2) { logger('corr', $fn); return; }
615616
616617
			system('flac', '--totally-silent', '--test', "$fn");
617618
			if ($? != 0 && $? != 2) { logger('corr', $fn); return; }
618619
619620
			return $hash;
620621
		}
621622
	}
622623
}
623624
624625
# Depending on which script mode is active,
625626
# set the @run array to the correct arguments.
626627
# This will be used to start the threads later.
627628
my @run;
628629
given ($mode) {
629630
	when ('index') {
630631
		@run = (\&md5index);
631632
	}
632633
	when ('test') {
633634
		@run = (\&md5test);
634635
	}
635636
}
636637
637638
# If script mode is either 'import' or 'double' we'll start only
638639
# one thread, else we'll start as many as the available number of CPUs.
639640
my @threads;
640641
if ($mode ne 'import' && $mode ne 'double') {
641642
	foreach (1 .. $cores) {
642643
		START_THREADS: push(@threads, threads->create(@run));
643644
	}
644645
}
645646
646647
# This loop is where the actual action takes place
647648
# (i.e. where all the subroutines get called from)
648649
foreach my $dn (@lib) {
649650
	if (-d $dn) {
650651
651652
		# Change directory to $dn.
652653
		chdir($dn)
653654
			or die "Can't change directory to '$dn': $!";
654655
655656
		# Start logging.
656657
		logger('start');
657658
658659
		# Initialize the database hash, and the files array.
659660
		# The init_hash function returns references.
660661
		my($files, $md5dbs) = init_hash($dn);
661662
662663
		given ($mode) {
663664
			when ('double') {
664665
				# Find identical files in database.
665666
				md5double();
666667
			}
667668
			when ('import') {
668669
669670
				# For all the files in $dn, run md5import.
670671
				foreach my $fn (@{$files}) { md5import($fn); }
671672
			}
672673
			when ('index') {
673674
674675
				foreach my $fn (@{$files}) {
675676
					if ($saw_sigint) { iquit(); }
676677
					while ($file_stack >= $disk_size) {
677678
						my $active = threads->running();
678679
						say("${active}: $file_stack > $disk_size");
679680
						yield();
680681
					}
681682
					# Unless file name exists in the database hash,
682683
					# continue.
683684
					unless ($md5h{$fn}) {
684685
						my $size = (stat($fn))[7];
685686
						if ($size && $size < $disk_size) {
686687
687688
							open(FILE, '<:raw', $fn) or die "Can't open '$fn': $!";
688689
							sysread(FILE, $file_contents{$fn}, $size);
689690
							close(FILE) or die "Can't close '$fn': $!";
690691
691692
							{ lock($file_stack);
692693
							$file_stack += length($file_contents{$fn}); }
693694
694695
							$q->enqueue($fn);
695696
						} elsif ($size) {
696697
							$large{$fn} = 1;
697698
						}
698699
					}
699700
				}
700701
			}
701702
			when ('test') {
702703
703704
				# Fetch all the keys for the database hash and put
704705
				# them in the queue.
705706
				foreach my $fn (sort(keys(%md5h))) {
706707
					if ($saw_sigint) { iquit(); }
707708
					while ($file_stack >= $disk_size) {
708709
						say("$file_stack > $disk_size");
709710
						yield();
710711
					}
711712
712713
					my $size = (stat($fn))[7];
713714
					if ($size && $size < $disk_size) {
714715
715716
						open(FILE, '<:raw', $fn) or die "Can't open '$fn': $!";
716717
						sysread(FILE, $file_contents{$fn}, $size);
717718
						close(FILE) or die "Can't close '$fn': $!";
718719
719720
						{ lock($file_stack);
720721
						$file_stack += length($file_contents{$fn}); }
721722
722723
						$q->enqueue($fn);
723724
					} elsif ($size) {
724725
						$large{$fn} = 1;
725726
					}
726727
				}
727728
			}
728729
		}
729730
730731
		if (%large) {
731732
			while ($file_stack > 0) {
732733
				say("$file_stack > 0");
733734
				yield();
734735
			}
735736
			foreach my $fn (sort(keys(%large))) {
736737
				$q->enqueue($fn);
737738
			}
738739
		}
739740
740741
#	use Digest::MD5;
741742
#	$md5 = Digest::MD5->new;
742743
#	$md5->add('foo', 'bar');
743744
#	$md5->add('baz');
744745
#	$digest = $md5->hexdigest;
745746
#	print "Digest is $digest\n";
746747
747748
		{ lock($stopping);
748749
		$stopping = 1; }
749750
750751
		foreach my $t (threads->list()) { $t->join(); }
751752
		#say("All threads joined");
752753
753754
		# Print the hash to the database file and close the log
754755
		hash2file();
755756
		logger('end', $n);
756757
	}
757758
}
758759

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