lumia

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

commit b2a3055319a2c8e8d2ba0f0ba05411c4d5dcf746
Author: lumidify <nobody@lumidify.org>
Date:   Fri, 21 Feb 2020 13:13:41 +0100

Initial Commit (old buggy code)

Diffstat:
ATODO | 2++
Alumia.pl | 829+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 831 insertions(+), 0 deletions(-)

diff --git a/TODO b/TODO @@ -0,0 +1,2 @@ +Allow to run command on multiple files but keep checksum (e.g. convmv) +update command - if file was edited diff --git a/lumia.pl b/lumia.pl @@ -0,0 +1,829 @@ +#!/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: 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; +use File::Spec::Functions qw(catfile catdir splitpath splitdir); +use File::Basename qw(basename dirname); +use File::Copy qw(move copy); +use File::Path qw(remove_tree make_path); +use String::ShellQuote; +use Cwd qw(getcwd); +use POSIX qw(SIGINT); +use Data::Dumper; +use Scalar::Util qw(looks_like_number); + +my $CKSUM_CMD = 'cksum -q'; +my $CKSUM_CHECK_CMD = 'cksum -c'; +my $CKSUM_CHECK_SINGLE_CMD = 'cksum -q'; + +sub clean_files { + my @files = ( + ".lumidify_archive_cksums", + ".lumidify_archive_cksums.cksum", + ".lumidify_archive_ignore", + ".lumidify_archive_dirs" + ); + for my $file (@files) { + if (-e $file) { + my $dir = getcwd(); + unlink($file, {safe => 1}) or return "ERROR: can't remove \"$dir/$file\": $!"; + print("Deleted \"$dir/$file\"\n"); + } + } + opendir(my $dh, ".") or die("ERROR: Unable to list files in \"" . getcwd() . "\"\n"); + my @dirs; + my $file; + while ($file = readdir($dh)) { + next if ($file =~ /\A\.\.?\z/); + # Just so these files aren't all left open while recursing + if (-d $file) { + push(@dirs, $file); + } + } + closedir($dh); + my $dir = getcwd(); + foreach (@dirs) { + chdir($_); + clean_files(); + chdir($dir); + } +} + +sub read_file { + my ($file, $is_cksums_file) = @_; + my $path = catfile(getcwd(), $file); + my %cksums; + my $fh; + if (!open($fh, "<", $file)) { + print(STDERR "ERROR: unable to open file \"$path\": $!\n"); + return; + } + my $in_fn = 0; + my $cur_cksum; + my $cur_str; + my $cur_fn = ""; + foreach (<$fh>) { + next if (!$in_fn && /^$/); + if ($is_cksums_file && !$in_fn) { + my @fields = split(/ /, $_, 3); + if ($#fields != 2) { + print(STDERR "ERROR: malformed line \"$_\" in file \"$path\"\n"); + next; + } + $cur_cksum = join(" ", @fields[0,1]); + $cur_str = $fields[2]; + } else { + $cur_str = $_; + } + my $bs = 0; + foreach my $ch (split(//, $cur_str)) { + if ($ch eq "\\") { + $bs++; + $cur_fn .= "\\" if !($bs %= 2) && $in_fn; + } elsif ($bs % 2) { + $cur_fn .= $ch if $in_fn; + $bs = 0; + } elsif ($ch eq "\"") { + if ($in_fn) { + $in_fn = 0; + $cksums{$cur_fn} = $cur_cksum; + $cur_fn = ""; + last; + } + $in_fn = 1; + } elsif ($in_fn) { + $cur_fn .= $ch; + } + } + } + if ($in_fn) { + print(STDERR "ERROR: unterminated filename in file \"$path\"\n"); + } + return \%cksums; +} + +# This would be much cleaner with cksums's -c option, but that +# doesn't seem to work with files beginning with whitespace +sub check_cksums { + my $cksum_file = shift; + my $cksums = read_file($cksum_file, 1); + foreach my $file (keys %$cksums) { + my $file_esc = shell_quote($file); + my $output = `$CKSUM_CHECK_SINGLE_CMD -- $file_esc 2>&1`; + my $path = catfile(getcwd(), $file); + if ($?) { + print(STDERR "ERROR getting cksum for file \"$path\":\n$output\n"); + next; + } + chomp($output); + print(($output eq $cksums->{$file} ? "OK" : "FAILED") . " $path\n"); + } + return; + my $fullpath = catfile(getcwd(), $cksum_file); + my $fh; + if (!open($fh, "<", $cksum_file)) { + print("ERROR: unable to open cksum file \"$fullpath\": $!\n"); + return; + } + foreach (<$fh>) { + chomp; + next if !$_; + my @fields = split(/ /, $_, 3); + if ($#fields != 2) { + print("ERROR: malformed line \"$_\" in file \"$fullpath\"\n"); + next; + } + my $cksum = join(" ", @fields[0,1]); + my $file = shell_quote($fields[2]); + # '--' needed so it works on files with leading '-' + my $output = `$CKSUM_CHECK_SINGLE_CMD -- $file`; + my $path = catfile(getcwd(), $fields[2]); + if ($?) { + print("ERROR getting cksum for file \"$path\":\n$output\n"); + next; + } + chomp($output); + print(($output eq $cksum ? "OK" : "FAILED") . " $path\n"); + } + close($fh); +} + +sub check_files { + check_cksums(".lumidify_archive_cksums.cksum"); + check_cksums(".lumidify_archive_cksums"); + #my $output = `$CKSUM_CHECK_CMD .lumidify_archive_cksums.cksum`; + #if ($?) { + # print("ERROR in directory \"" . getcwd() . "\"\n"); + #} + # It would be better to do this properly with cksum printing its output + # directly, but I couldn't get system() to work with quitting if SIGINT + # is received (yes, I followed the instructions in perldoc -f system) + #print($output); + #if (-s ".lumidify_archive_cksums") { + # $output = `$CKSUM_CHECK_CMD .lumidify_archive_cksums`; + # if ($?) { + # print("ERROR in directory \"" . getcwd() . "\"\n"); + # } + # print($output); + #} + return if (!-f ".lumidify_archive_dirs"); + my $dirs = read_file(".lumidify_archive_dirs"); + foreach my $dir (keys %$dirs) { + my $cwd = getcwd(); + if (!-d $dir) { + print("ERROR: directory \"$cwd/$dir\" does not exist anymore.\n"); + next; + } + chdir($dir); + check_files(); + chdir($cwd); + } + return; + open(my $fh, "<", ".lumidify_archive_dirs") or die("ERROR: Unable to open \"" . getcwd() . "/.lumidify_archive_dirs\"\n"); + my @dirs; + foreach (<$fh>) { + chomp; + next if (!$_); + # Just so these files aren't all left open while recursing + push(@dirs, $_); + } + close($fh); + foreach (@dirs) { + my $dir = getcwd(); + if (!-d $_) { + print("ERROR: directory \"$dir/$_\" does not exist anymore.\n"); + next; + } + chdir($_); + check_files(); + chdir($dir); + } +} + +sub check_new_files_recurse { + my $dir = shift; + my $cur_hash = shift; + my $cur_cksum_hash = shift; + #my %ignore; + my $ignore = {}; + if (-f catfile($dir, '.lumidify_archive_ignore')) { + $ignore = read_file(catfile($dir, ".lumidify_archive_ignore")); + } +=pod + open(my $fh, "<", catfile($dir, '.lumidify_archive_ignore')) or die("Can't open $dir/.lumidify_archive_ignore: $!\n"); + foreach (<$fh>) { + chomp; + $ignore{$_} = undef; + } + close($fh); + } +=cut + opendir(my $dh, $dir) or die("Can't open $dir: $!\n"); + my $file; + my $fullpath; + while ($file = readdir($dh)) { + next if ($file =~ /\A\.\.?\z/); + next if (exists($ignore->{$file})); + next if ($file eq '.lumidify_archive_cksums' || + $file eq '.lumidify_archive_cksums.cksum' || + $file eq '.lumidify_archive_dirs' || + $file eq '.lumidify_archive_ignore'); + $fullpath = catfile($dir, $file); + if (-d $fullpath) { + $cur_hash->{$file} = {}; + if (!defined($cur_cksum_hash) || !exists($cur_cksum_hash->{$file})) { + check_new_files_recurse($fullpath, $cur_hash->{$file}, undef); + } else { + check_new_files_recurse($fullpath, $cur_hash->{$file}, $cur_cksum_hash->{$file}); + } + } else { + if (!defined($cur_cksum_hash) || !exists($cur_cksum_hash->{$file})) { + $cur_hash->{$file} = undef; + } + } + } + closedir($dh); +} + +sub check_new_files { + my $dir = shift; + my $cksum_tree = shift; + my %new_hash; + check_new_files_recurse($dir, \%new_hash, $cksum_tree); + return \%new_hash; +} + +sub add_new_files_recurse { + my $dir = shift; + my $cur_hash = shift; + my $cur_cksum_hash = shift; + my $fullpath; + my $cksum_output; + foreach my $file (keys(%$cur_hash)) { + $fullpath = catfile($dir, $file); + if (-d $fullpath) { + if (!exists($cur_cksum_hash->{$file})) { + $cur_cksum_hash->{$file} = {}; + } + add_new_files_recurse($fullpath, $cur_hash->{$file}, $cur_cksum_hash->{$file}); + } else { + my $path_esc = shell_quote($fullpath); + $cksum_output = `$CKSUM_CMD -- $path_esc`; + chomp($cksum_output); + # TODO: check for error + $cur_cksum_hash->{$file} = $cksum_output; + print("Added checksum: $cksum_output $fullpath\n"); + } + } +} + +sub add_new_files { + my $dir = shift; + my $new_files = shift; + my $cksum_tree = shift; + add_new_files_recurse($dir, $new_files, $cksum_tree); +} + +sub check_add_new_files { + my $dir = shift; + my $cksum_tree = shift; + my $new_files = check_new_files($dir, $cksum_tree); + add_new_files($dir, $new_files, $cksum_tree); + write_cksums($dir, $cksum_tree); +} + +sub read_cksums_old { + my ($cksums, $dir, $recurse) = @_; + my $cksums_file = catfile($dir, '.lumidify_archive_cksums'); + if (!-f $cksums_file) { + die("No cksum file '.lumidify_archive_cksums' in directory $dir\n"); + } + open(my $fh, "<", $cksums_file) or die("Can't open $cksums_file: $!\n"); + foreach (<$fh>) { + chomp; + next if (!$_); + my @fields = split(/ /, $_, 3); + if ($#fields != 2 || !looks_like_number($fields[0])) { + print("ERROR: Malformed line in $cksums_file:\n$_\n"); + next; + } + $cksums->{$fields[2]} = $fields[0] . " " . $fields[1]; + } + close($fh); + my $dirs_file = catfile($dir, '.lumidify_archive_dirs'); + return if (!-f $dirs_file); + open($fh, "<", $dirs_file) or die("Can't open $dirs_file: $!\n"); + foreach (<$fh>) { + chomp; + next if (!$_); + my $fulldir = catdir($dir, $_); + if (!-d $fulldir) { + print(STDERR "WARNING: Directory \"$fulldir\" mentioned in \"$dirs_file\" does not exist!\n"); + } + $cksums->{$_} = {}; + if ($recurse) { + read_cksums_old($cksums->{$_}, $fulldir, 1); + } + } + close($fh); +} + +sub read_cksums { + my ($cksums, $dir, $recurse) = @_; + my $cksums_file = catfile($dir, '.lumidify_archive_cksums'); + if (!-f $cksums_file) { + die("No cksum file '.lumidify_archive_cksums' in directory $dir\n"); + } + my $tmp_cksums = read_file($cksums_file, 1); + foreach (keys %$tmp_cksums) { + $cksums->{$_} = $tmp_cksums->{$_}; + } + #$cksums->{keys %$tmp_cksums} = @{$tmp_cksums->{keys %$tmp_cksums}}; +=pod + open(my $fh, "<", $cksums_file) or die("Can't open $cksums_file: $!\n"); + foreach (<$fh>) { + chomp; + next if (!$_); + my @fields = split(/ /, $_, 3); + if ($#fields != 2) { + print("ERROR: Malformed line in $cksums_file:\n$_\n"); + next; + } + $cksums->{$fields[2]} = $fields[0] . " " . $fields[1]; + } + close($fh); +=cut + my $dirs_file = catfile($dir, '.lumidify_archive_dirs'); + return if (!-f $dirs_file); + my $dirs = read_file($dirs_file); + foreach (keys %$dirs) { + my $fulldir = catdir($dir, $_); + if (!-d $fulldir) { + print(STDERR "WARNING: Directory \"$fulldir\" mentioned in \"$dirs_file\" does not exist!\n"); + } + $cksums->{$_} = {}; + if ($recurse) { + read_cksums($cksums->{$_}, $fulldir, 1); + } + } +=pod + open($fh, "<", $dirs_file) or die("Can't open $dirs_file: $!\n"); + foreach (<$fh>) { + chomp; + next if (!$_); + my $fulldir = catdir($dir, $_); + if (!-d $fulldir) { + print(STDERR "WARNING: Directory \"$fulldir\" mentioned in \"$dirs_file\" does not exist!\n"); + } + $cksums->{$_} = {}; + if ($recurse) { + read_cksums($cksums->{$_}, $fulldir, 1); + } + } + close($fh); +=cut +} + +sub init_cksums_old { + my $dir = shift; + # avoid catfile turning it into an absolute path if $dir is an empty string + if (!$dir) { + $dir = "."; + } + my %cksums; + read_cksums_old(\%cksums, $dir, 1); + return \%cksums; +} + +sub init_cksums { + my $dir = shift; + # avoid catfile turning it into an absolute path if $dir is an empty string + if (!$dir) { + $dir = "."; + } + my %cksums; + read_cksums(\%cksums, $dir, 1); + return \%cksums; +} + +# TODO: possibly sort files before dumping in files? +sub write_cksums { + my ($dir, $cur_cksum_hash, $recurse, $create_dirs) = @_; + # FIXME: error checking + if ($create_dirs) { + make_path($dir); + } + my $cksums_path = catfile($dir, ".lumidify_archive_cksums"); + my $dirs_path = catfile($dir, ".lumidify_archive_dirs"); + open(my $cksumsfh, ">", $cksums_path) or die("Can't open \"$cksums_path\" for writing: $!\n"); + open(my $dirsfh, ">", $dirs_path) or die("Can't open \"$dirs_path\" for writing: $!\n"); + foreach my $file (keys %$cur_cksum_hash) { + my $file_esc = $file; + $file_esc =~ s/\\/\\\\/g; + $file_esc =~ s/"/\\"/g; + if (ref($cur_cksum_hash->{$file}) eq "HASH") { + print($dirsfh "\"$file_esc\"\n"); + } else { + print($cksumsfh "$cur_cksum_hash->{$file} \"$file_esc\"\n"); + } + } + close($cksumsfh); + close($dirsfh); + + open(my $fh, ">", "$cksums_path.cksum") or die("Cannot open $cksums_path.cksum for writing\n"); + my $cksums_path_esc = shell_quote($cksums_path); + my $dirs_path_esc = shell_quote($dirs_path); + my $cksums_cksum = `$CKSUM_CMD $cksums_path_esc`; + my $dirs_cksum = `$CKSUM_CMD $dirs_path_esc`; + chomp($cksums_cksum); + chomp($dirs_cksum); + print($fh "$cksums_cksum \".lumidify_archive_cksums\"\n"); + print($fh "$dirs_cksum \".lumidify_archive_dirs\"\n"); + if (-f catfile($dir, '.lumidify_archive_ignore')) { + my $ignore_path_esc = shell_quote(catfile($dir, '.lumidify_archive_ignore')); + my $ignore_cksum = `$CKSUM_CMD $ignore_path_esc`; + chomp $ignore_cksum; + print($fh "$ignore_cksum \".lumidify_archive_ignore\"\n"); + } + close($fh); + + # For e.g. moving, we don't want to read and write all cksums since + # everything below the level being moved stays the same + return if (!$recurse); + # Use second pass for this so the files don't all stay open + foreach my $file (keys %$cur_cksum_hash) { + if (ref($cur_cksum_hash->{$file}) eq "HASH") { + write_cksums(catdir($dir, $file), $cur_cksum_hash->{$file}, 1, $create_dirs); + } + } +} + +# $src: list of source paths +# $dst: destination directory or file (in latter case only one src is allowed) +sub copy_files { + my $src = shift; + my $dst = shift; + my $dst_dir = $dst; + if (!-d $dst) { + $dst_dir = dirname($dst); + } + my $diff_name = 0; + if (!-d $dst && !-d $src->[0]) { + $diff_name = 1; + } + if (!-e $dst && -d $src->[0]) { + $diff_name = 1; + } + if (system("cp", "-aiv", @$src, $dst)) { + die("ERROR while copying files\n"); + } + # Separate files by current dir so the cksum and dir files only need to be opened once + my %src_files; + foreach my $src_file (@$src) { + my $dir = dirname($src_file); + if (!exists($src_files{$dir})) { + $src_files{$dir} = []; + } + push(@{$src_files{$dir}}, basename($src_file)); + } + my %dst_cksums; + read_cksums(\%dst_cksums, $dst_dir, 0); + foreach my $src_dir (keys %src_files) { + my %src_cksums; + read_cksums(\%src_cksums, $src_dir, 0); + foreach my $src_file (@{$src_files{$src_dir}}) { + if (exists($src_cksums{$src_file})) { + if ($diff_name) { + $dst_cksums{basename($dst)} = $src_cksums{$src_file}; + } else { + $dst_cksums{$src_file} = $src_cksums{$src_file}; + } + } else { + print(STDERR "WARNING: \"$dst_dir/$src_file\" not in cksum or directory list.\n"); + } + } + } + write_cksums($dst_dir, \%dst_cksums, 0); +} + +sub move_file { + my ($src, $dst) = @_; + if (-d $dst) { + $dst = catdir($dst, basename($src)); + } + if (-e $dst) { + print(STDERR "WARNING: \"$dst\" exists already. Do you want to replace it? "); + my $choice = <STDIN>; + chomp $choice; + if ($choice ne "y" && $choice ne "Y") { + return "Not moving \"$src\" to \"$dst\"."; + } + } + move($src, $dst) or return "ERROR: can't move \"$src\" to \"$dst\": $!"; + return 0; +} + +sub move_from_same_dir { + my ($src_dir, $src_files, $dst_dir) = @_; + my %src_cksums; + read_cksums(\%src_cksums, $src_dir, 0); + my %dst_cksums; + read_cksums(\%dst_cksums, $dst_dir, 0); + foreach my $src_file (@$src_files) { + my $fullpath = catfile($src_dir, $src_file); + if (my $err = move_file($fullpath, $dst_dir)) { + print(STDERR "$err\n"); + next; + } + if (exists($src_cksums{$src_file})) { + $dst_cksums{$src_file} = $src_cksums{$src_file}; + delete($src_cksums{$src_file}); + } else { + print(STDERR "WARNING: \"$dst_dir/$src_file\" not in cksum or directory list.\n"); + } + } + write_cksums($src_dir, \%src_cksums, 0); + write_cksums($dst_dir, \%dst_cksums, 0); +} + +sub move_rename { + my ($src, $dst) = @_; + my $src_dir = dirname($src); + my $dst_dir = dirname($dst); + my $src_file = basename($src); + my $dst_file = basename($dst); + my %src_cksums; + read_cksums(\%src_cksums, $src_dir, 0); + my %dst_cksums; + # if a file is simply being renamed in the same dir, the cksums + # should only be loaded and written once + if ($src_dir eq $dst_dir) { + %dst_cksums = %src_cksums; + delete($dst_cksums{$src_file}); + } else { + read_cksums(\%dst_cksums, $dst_dir, 0); + } + if (my $err = move_file($src, $dst)) { + print(STDERR "$err\n"); + return; + } + if (exists($src_cksums{$src_file})) { + $dst_cksums{$dst_file} = $src_cksums{$src_file}; + delete($src_cksums{$src_file}); + } else { + print(STDERR "WARNING: \"$dst\" not in cksum or directory list.\n"); + } + if ($src_dir ne $dst_dir) { + write_cksums($src_dir, \%src_cksums, 0); + } + write_cksums($dst_dir, \%dst_cksums, 0); +} + +sub cmp_path { + my ($src, $dst) = @_; + # remove trailing slash so compare works + $src =~ s/\/$//; + $dst =~ s/\/$//; + return $src eq $dst; +} + +# $src: list of source paths +# $dst: destination directory or file (in latter case only one src is allowed) +sub move_files { + my $src = shift; + my $dst = shift; + foreach my $src_file (@$src) { + if ($src_file eq ".") { + die("Can't move current directory (.)\n"); + } + } + if (!-d $dst && $#$src != 0) { + die("move: only one src argument allowed when dst is a file\n"); + } + if (!-d $dst && !-d $src->[0]) { + move_rename($src->[0], $dst); + return; + } + if (!-e $dst && -d $src->[0]) { + move_rename($src->[0], $dst); + return; + } + if (-e $dst && !-d $dst && -d $src->[0]) { + die("move: can't move directory to file\n"); + } + # Separate files by current dir so the cksum and dir files only need to be opened once + my %src_files; + foreach my $src_file (@$src) { + if (!-e $src_file) { + die("Source file \"$src_file\" doesn't exist.\n"); + } + if (cmp_path($src_file, $dst)) { + print(STDERR "ERROR: can't move \"$src_file\" into \"$dst\" (same dir)\n"); + next; + } + my $dir = dirname($src_file); + if (!exists($src_files{$dir})) { + $src_files{$dir} = []; + } + push(@{$src_files{$dir}}, basename($src_file)); + } + foreach my $src_dir (keys %src_files) { + move_from_same_dir($src_dir, $src_files{$src_dir}, $dst); + } +} + +sub remove_file_dir { + my $path = shift; + if (-d $path) { + remove_tree($path, {safe => 1}) or return "ERROR: can't remove \"$path\": $!"; + } else { + unlink($path, {safe => 1}) or return "ERROR: can't remove \"$path\": $!"; + } + return 0; +} + +sub remove_from_same_dir { + my ($dir, $files) = @_; + my %cksums; + read_cksums(\%cksums, $dir, 0); + foreach my $file (@$files) { + my $fullpath = catfile($dir, $file); + if (!-e $fullpath) { + print(STDERR "\"$fullpath\": No such file or directory.\n"); + } + if (my $err = remove_file_dir($fullpath)) { + print(STDERR "$err\n"); + next; + } + if (exists($cksums{$file})) { + delete($cksums{$file}); + } else { + print(STDERR "WARNING: \"$file\" not in cksum or directory list.\n"); + } + } + write_cksums($dir, \%cksums, 0); +} + +sub remove_files { + my $files = shift; + my %sorted_files; + foreach my $file (@$files) { + my $dir = dirname($file); + if (!exists($sorted_files{$dir})) { + $sorted_files{$dir} = []; + } + push(@{$sorted_files{$dir}}, basename($file)); + } + foreach my $dir (keys %sorted_files) { + remove_from_same_dir($dir, $sorted_files{$dir}); + } +} + +sub make_dirs { + my @created_dirs; + foreach (@_) { + if (system("mkdir", $_)) { + print(STDERR "ERROR creating directory $_\n"); + } + push(@created_dirs, $_); + } + # Separate files by current dir so the cksum and dir files only need to be opened once + my %dirs; + foreach my $dir (@created_dirs) { + # FiXME: seems a bit ugly (must write cksums in new dir for it to work later) + write_cksums($dir, {}, 0); + my $parent = dirname($dir); + if (!exists($dirs{$parent})) { + $dirs{$parent} = []; + } + push(@{$dirs{$parent}}, basename($dir)); + } + foreach my $parent (keys %dirs) { + my %cksums; + read_cksums(\%cksums, $parent, 0); + foreach my $dir (@{$dirs{$parent}}) { + $cksums{$dir} = {}; + } + write_cksums($parent, \%cksums, 0); + } +} + +if ($#ARGV < 0) { + die("USAGE: test.pl {init|check|clean|cknew|addnew|cp|mv|rm|mkdir}\n"); +} +if ($ARGV[0] eq "mv") { + if ($#ARGV < 2) { + die "mv requires at least two arguments\n"; + } + my @src = @ARGV[1..$#ARGV-1]; + move_files(\@src, $ARGV[-1]); +} elsif ($ARGV[0] eq "rm") { + if ($#ARGV < 1) { + die "rm requires at least one argument\n"; + } + my @files = @ARGV[1..$#ARGV]; + remove_files(\@files); +} elsif ($ARGV[0] eq "addnew") { + my $dir = "."; + if ($#ARGV > 0) { + $dir = $ARGV[1]; + } + my $cksums = init_cksums($dir); + check_add_new_files($dir, $cksums); + write_cksums($dir, $cksums, 1); +} elsif ($ARGV[0] eq "cknew") { + my $dir = "."; + if ($#ARGV > 0) { + $dir = $ARGV[1]; + } + my $cksums = init_cksums($dir); + my $new_files = check_new_files($dir, $cksums); + print(Dumper($new_files)); +} elsif ($ARGV[0] eq "init") { + my $dir = "."; + if ($#ARGV > 0) { + $dir = $ARGV[1]; + } + # FIXME: at least first recurse through dirs and check if any contain cksums + my $cksums = {}; + check_add_new_files($dir, $cksums); + write_cksums($dir, $cksums, 1); +} elsif ($ARGV[0] eq "check") { + if ($#ARGV > 0) { + my $dir = $ARGV[1]; + if (!-d $dir) { + die("ERROR: Directory \"$dir\" does not exist.\n"); + } + chdir($dir); + } + check_files(); +} elsif ($ARGV[0] eq "clean") { + if ($#ARGV > 0) { + my $dir = $ARGV[1]; + if (!-d $dir) { + die("ERROR: Directory \"$dir\" does not exist.\n"); + } + chdir($dir); + } + clean_files(); +} elsif ($ARGV[0] eq "extract") { + my $src_dir = "."; + my $dst_dir; + if ($#ARGV > 1) { + $src_dir = $ARGV[1]; + $dst_dir = $ARGV[2]; + } elsif ($#ARGV == 1) { + $dst_dir = $ARGV[1]; + } else { + die("ERROR: `extract` requires at least a destination directory.\n"); + } + if (!-d $src_dir) { + die("ERROR: Directory \"$src_dir\" does not exist.\n"); + } + if (!-d $dst_dir) { + die("ERROR: Directory \"$dst_dir\" does not exist.\n"); + } + # FIXME: check .lumidify_archive_cksums.cksum when initializing + my $cksums = init_cksums($src_dir); + write_cksums($dst_dir, $cksums, 1, 1); +} elsif ($ARGV[0] eq "cp") { + if ($#ARGV < 2) { + die "cp requires at least two arguments\n"; + } + my @src = @ARGV[1..$#ARGV-1]; + copy_files(\@src, $ARGV[-1]); +} elsif ($ARGV[0] eq "mkdir") { + if ($#ARGV < 1) { + die "mkdir requires at least one argument\n"; + } + my @dirs = @ARGV[1..$#ARGV]; + make_dirs(@dirs); +} elsif ($ARGV[0] eq "convert") { + my $dir = "."; + if ($#ARGV > 0) { + $dir = $ARGV[1]; + } + my $cksums = init_cksums_old($dir); + write_cksums($dir, $cksums, 1); +}