commit a964a391d65fce7963a55c9964bbbe6a5520a2ff
parent 3d69b3355b084a09aaa2a64a4b7d47e9296d4b01
Author: lumidify <nobody@lumidify.org>
Date:   Mon, 23 Mar 2020 15:33:23 +0100
Fixes; allow to check only individual files
Diffstat:
| M | lumia.pl |  |  | 143 | +++++++++++++++++++++++++++++++++++++++++++------------------------------------ | 
1 file changed, 78 insertions(+), 65 deletions(-)
diff --git a/lumia.pl b/lumia.pl
@@ -1,25 +1,16 @@
 #!/usr/bin/env perl
 
-# FIXME: add ckold to check what files don't exist anymore and remove them from cksums
-# FIXME: write cksums as we go along during check_add_new, not just at end
 # FIXME: some way to avoid writing .lumidify* in dirs but still index them? e.g. Code/CMSG
 # FIXME: cksum don't create malformed line if permission denied
-# FIXME: dirs in ignore still created when extracting
 # FIXME: make generic function to traverse dirs and call other function on each dir
-# FIXME: extract - actually copy files, don't write again and regen cksums.cksum
-# FIXME: check cksums.cksum when reading files
-# FIXME: check if .lumidify* are writable first so cksums aren't generated uselessly
 # FIXME: handle rm, etc. on .lumidify* files
-# FIXME: handle files with newline?
 # FIXME: ignore all except for a certain file/folder
-# FIXME: store modified date and recksum filed with changed date
+# FIXME: store modified date and checksum filed with changed date
 # FIXME: sha256 works with leading zeroes, maybe switch?
 # FIXME: allow different hash types
 # FIXME: don't write anything if cksum fails (will create malformed line)
 # FIXME: add option to just check dir structure or maybe check if everything exists
-# WARNING: convert regenerates cksums.cksum
 # FIXME: add option to compare cksums of two dirs
-# FIXME: add option to check only individual files
 
 use strict;
 use warnings;
@@ -34,12 +25,21 @@ use Data::Dumper;
 use Scalar::Util qw(looks_like_number);
 use Getopt::Long;
 
+# the file used to store checksums for files
+my $CKSUM_FILE = ".lumidify_archive_cksums";
+# the file used to store directory names
+my $DIR_FILE = ".lumidify_archive_dirs";
+# the file read to ignore files or directories
+my $IGNORE_FILE = ".lumidify_archive_ignore";
+# the file containing checksums of $CKSUM_FILE and $DIR_FILE
+my $DOUBLE_CKSUM_FILE = ".lumidify_archive_cksums.cksum";
+
 my $CKSUM_CMD = 'cksum -q';
 my %SPECIAL_FILES = (
-	".lumidify_archive_cksums" => 1,
-	".lumidify_archive_cksums.cksum" => 1,
-	".lumidify_archive_ignore" => 1,
-	".lumidify_archive_dirs" => 1
+	$CKSUM_FILE => 1,
+	$DIR_FILE => 1,
+	$IGNORE_FILE => 1,
+	$DOUBLE_CKSUM_FILE => 1
 );
 
 # escape a filename for writing into the checksum files
@@ -86,18 +86,14 @@ sub make_file_iter_basic {
 	}, @files;
 }
 
-# make a basic directory iterator, which only returns all directories
-sub make_dir_iter {
-	make_file_iter_basic sub {-d $_[0]}, @_;
-}
-
 # make an interator that only returns the directories which are present
-# in the .lumidify_archive_dirs files
+# in the $DIR_FILE files, in addition to the files and directories that
+# were originally passed as arguments
 # note: this returns nonexistent directories if those are still
 # specified in the lumia files
 sub make_lumia_iter {
 	make_file_iter sub {1}, sub {
-		my $path = "$_[0]/.lumidify_archive_dirs";
+		my $path = "$_[0]/$DIR_FILE";
 		return [] if !-f $path;
 		my $dirs = read_file($path, {});
 		return if !defined $dirs;
@@ -117,10 +113,7 @@ sub make_lumia_iter {
 # remove all special lumia files from the given directory
 sub clean_files {
 	my $dir = shift;
-	my $match_lumia_files = sub {
-		exists $SPECIAL_FILES{basename $_[0]};
-	};
-	my $iter = make_file_iter $match_lumia_files, $dir;
+	my $iter = make_file_iter_basic sub {exists $SPECIAL_FILES{basename $_[0]};}, $dir;
 	while (my $file = $iter->()) {
 		if (!unlink $file) {
 			warn "WARNING: Unable to remove file \"$file\"!";
@@ -202,9 +195,9 @@ sub read_cksum_file {
 # read the checksums and directory names in $dir
 sub read_cksums {
 	my $dir = shift;
-	my $cksums = read_cksum_file("$dir/.lumidify_archive_cksums", {});
+	my $cksums = read_cksum_file("$dir/$CKSUM_FILE", {});
 	return undef if !defined $cksums;
-	$cksums = read_file("$dir/.lumidify_archive_dirs", $cksums);
+	$cksums = read_file("$dir/$DIR_FILE", $cksums);
 	return undef if !defined $cksums;
 	return $cksums;
 }
@@ -246,19 +239,40 @@ sub check_cksums {
 
 # check the checksums of all files in $top_dir
 sub check_files {
-	my $top_dir = shift;
-	my $iter = make_lumia_iter $top_dir;
-	while (my $dir = $iter->()) {
-		check_cksums $dir, ".lumidify_archive_cksums.cksum";
-		check_cksums $dir, ".lumidify_archive_cksums";
+	my $iter = make_lumia_iter @_;
+	while (my $file = $iter->()) {
+		if (-d $file) {
+			check_cksums $file, "$DOUBLE_CKSUM_FILE";
+			check_cksums $file, "$CKSUM_FILE";
+		} else {
+			my $dir = dirname $file;
+			my $base = basename $file;
+			if (exists $SPECIAL_FILES{$base}) {
+				warn "ERROR: File is reserved for lumia.pl: $file\n";
+				next;
+			}
+			my $cksums = read_cksum_file("$dir/$CKSUM_FILE");
+			next if !defined $cksums;
+			if (!exists $cksums->{$base}) {
+				warn "ERROR: File doesn't exist in checksums: $file\n";
+				next;
+			}
+			my $output = get_cksum "$file";
+			next if !defined $output;
+			if ($output eq $cksums->{$base}) {
+				print "OK $file\n";
+			} else {
+				print "FAILED $file\n";
+			}
+		}
 	}
 }
 
 # write the checksums of the special lumia files given as arguments
-# to ".lumidify_archive_cksums.cksum" in $dir
+# to "$DOUBLE_CKSUM_FILE" in $dir
 sub write_special_cksums {
 	my ($dir, @files) = @_;
-	my $cksum_file = "$dir/.lumidify_archive_cksums.cksum";
+	my $cksum_file = "$dir/$DOUBLE_CKSUM_FILE";
 	my $cksums = {};
 	if (-f $cksum_file) {
 		$cksums = read_cksum_file $cksum_file, {};
@@ -276,7 +290,7 @@ sub write_special_cksums {
 # - if $file_func is set, it is called for each new file
 # - if $before_dir_func is set, it is called before processing the
 #   files in each directory that has new files OR if a directory
-#   is entirely new (well, it only checks if ".lumidify_archive_cksums.cksum" exists)
+#   is entirely new (well, it only checks if "$DOUBLE_CKSUM_FILE" exists)
 # - if $after_dir_func is set, it is called after processing the
 #   files in each directory that has new files
 sub check_new_files {
@@ -294,9 +308,9 @@ sub check_new_files {
 			}
 			return {};
 		};
-		my $ignore = $read_file_noerror->("$dir/.lumidify_archive_ignore", \&read_file);
-		my $lumia_dirs = $read_file_noerror->("$dir/.lumidify_archive_dirs", \&read_file);
-		my $lumia_files = $read_file_noerror->("$dir/.lumidify_archive_cksums", \&read_cksum_file);
+		my $ignore = $read_file_noerror->("$dir/$IGNORE_FILE", \&read_file);
+		my $lumia_dirs = $read_file_noerror->("$dir/$DIR_FILE", \&read_file);
+		my $lumia_files = $read_file_noerror->("$dir/$CKSUM_FILE", \&read_cksum_file);
 		my @dirs;
 		my $found = 0;
 		while (my $file = readdir $dh) {
@@ -317,7 +331,7 @@ sub check_new_files {
 		}
 		closedir $dh;
 		# also call $before_dir_func if the directory has not been initialized yet
-		if (!$found && !-f "$dir/.lumidify_archive_cksums.cksum" && defined $before_dir_func) {
+		if (!$found && !-f "$dir/$DOUBLE_CKSUM_FILE" && defined $before_dir_func) {
 			$before_dir_func->($dir);
 		}
 		if ($found && defined $after_dir_func) {
@@ -338,7 +352,7 @@ sub check_add_new_files {
 		my ($dir, $file) = @_;
 		my $fullpath = "$dir/$file";
 		if (-d $fullpath) {
-			my $dir_file = "$dir/.lumidify_archive_dirs";
+			my $dir_file = "$dir/$DIR_FILE";
 			my $fh;
 			if (!open $fh, ">>", $dir_file) {
 				warn "ERROR: Unable to append to file \"$dir_file\"!";
@@ -350,7 +364,7 @@ sub check_add_new_files {
 		} else {
 			my $cksum_output = get_cksum $fullpath;
 			return if !defined $cksum_output;
-			my $cksum_file = "$dir/.lumidify_archive_cksums";
+			my $cksum_file = "$dir/$CKSUM_FILE";
 			my $fh;
 			if (!open $fh, ">>", $cksum_file) {
 				warn "ERROR: Unable to append to file \"$cksum_file\"!";
@@ -362,8 +376,8 @@ sub check_add_new_files {
 		}
 		print "Added \"$fullpath\"\n";
 	}, sub {
-		if (-f "$_[0]/.lumidify_archive_cksums.cksum") {
-			if (!check_cksums $_[0], ".lumidify_archive_cksums.cksum", 1) {
+		if (-f "$_[0]/$DOUBLE_CKSUM_FILE") {
+			if (!check_cksums $_[0], "$DOUBLE_CKSUM_FILE", 1) {
 				warn "Checksum files corrupt in \"$_[0]\", not adding new checksums!\n";
 				return 0;
 			}
@@ -373,11 +387,11 @@ sub check_add_new_files {
 		return 1;
 	}, sub {
 		if ($changed_dirs) {
-			write_special_cksums $_[0], ".lumidify_archive_dirs";
+			write_special_cksums $_[0], "$DIR_FILE";
 			$changed_dirs = 0;
 		}
 		if ($changed_files) {
-			write_special_cksums $_[0], ".lumidify_archive_cksums";
+			write_special_cksums $_[0], "$CKSUM_FILE";
 			$changed_files = 0;
 		}
 	};
@@ -417,13 +431,13 @@ sub write_cksums {
 	# No, this isn't efficient...
 	if ($files_modified) {
 		my %file_cksums = map {$_ => $contents->{$_}} grep({defined $contents->{$_}} keys %$contents);
-		write_cksum_file("$dir/.lumidify_archive_cksums", \%file_cksums);
-		write_special_cksums $dir, ".lumidify_archive_cksums";
+		write_cksum_file("$dir/$CKSUM_FILE", \%file_cksums);
+		write_special_cksums $dir, "$CKSUM_FILE";
 	}
 	if ($dirs_modified) {
 		my %dir_cksums = map {$_ => undef} grep({!defined $contents->{$_}} keys %$contents);
-		write_file "$dir/.lumidify_archive_dirs", \%dir_cksums;
-		write_special_cksums $dir, ".lumidify_archive_dirs";
+		write_file "$dir/$DIR_FILE", \%dir_cksums;
+		write_special_cksums $dir, "$DIR_FILE";
 	}
 }
 
@@ -434,7 +448,7 @@ sub check_old_files {
 	while (my $dir = $iter->()) {
 		# if $dir doesn't exist, the iterator already issued a warning
 		if (-e $dir) {
-			my $cksums = read_cksum_file("$dir/.lumidify_archive_cksums", {}) // {};
+			my $cksums = read_cksum_file("$dir/$CKSUM_FILE", {}) // {};
 			foreach my $file (keys %$cksums) {
 				if (!-e "$dir/$file") {
 					warn "Nonexistent file: \"$dir/$file\"!\n";
@@ -453,26 +467,26 @@ sub remove_old_files {
 		if (!-e $dir) {
 			my $parent = dirname $dir;
 			my $child = basename $dir;
-			my $lumia_dirs = read_file("$parent/.lumidify_archive_dirs", {}) // {};
+			my $lumia_dirs = read_file("$parent/$DIR_FILE", {}) // {};
 			if (exists $lumia_dirs->{$child}) {
 				delete $lumia_dirs->{$child};
-				write_file "$parent/.lumidify_archive_dirs", $lumia_dirs;
-				print "Removed \"$dir\" from \"$parent/.lumidify_archive_dirs\"\n";
-				write_special_cksums $parent, ".lumidify_archive_dirs";
+				write_file "$parent/$DIR_FILE", $lumia_dirs;
+				print "Removed \"$dir\" from \"$parent/$DIR_FILE\"\n";
+				write_special_cksums $parent, "$DIR_FILE";
 			}
 		} else {
-			my $cksums = read_cksum_file("$dir/.lumidify_archive_cksums", {}) // {};
+			my $cksums = read_cksum_file("$dir/$CKSUM_FILE", {}) // {};
 			my $found = 0;
 			foreach my $file (keys %$cksums) {
 				if (!-e "$dir/$file") {
 					delete $cksums->{$file};
-					print "Removed \"$dir/$file\" from \"$dir/.lumidify_archive_cksums\"\n";
+					print "Removed \"$dir/$file\" from \"$dir/$CKSUM_FILE\"\n";
 					$found = 1;
 				}
 			}
 			if ($found) {
-				write_cksum_file "$dir/.lumidify_archive_cksums", $cksums;
-				write_special_cksums $dir, ".lumidify_archive_cksums";
+				write_cksum_file "$dir/$CKSUM_FILE", $cksums;
+				write_special_cksums $dir, "$CKSUM_FILE";
 			}
 		}
 	}
@@ -775,13 +789,13 @@ sub make_dirs {
 		push(@{$dirs{$parent}}, basename($dir));
 	}
 	foreach my $parent (keys %dirs) {
-		my $parent_dirs = read_file "$parent/.lumidify_archive_dirs", {};
+		my $parent_dirs = read_file "$parent/$DIR_FILE", {};
 		next if !defined $parent_dirs;
 		foreach my $dir (@{$dirs{$parent}}) {
 			$parent_dirs->{$dir} = "";
 		}
-		write_file "$parent/.lumidify_archive_dirs", $parent_dirs;
-		write_special_cksums $parent, ".lumidify_archive_dirs";
+		write_file "$parent/$DIR_FILE", $parent_dirs;
+		write_special_cksums $parent, "$DIR_FILE";
 	}
 }
 
@@ -842,11 +856,10 @@ if ($ARGV[0] eq "mv") {
 	}
 	remove_old_files $dir;
 } elsif ($ARGV[0] eq "check") {
-	my $dir = ".";
-	if ($#ARGV > 0) {
-		$dir = $ARGV[1];
+	if ($#ARGV < 1) {
+		die "At least one path required";
 	}
-	check_files $dir;
+	check_files @ARGV[1..$#ARGV];
 } elsif ($ARGV[0] eq "clean") {
 	my $dir = ".";
 	if ($#ARGV > 0) {